diff options
329 files changed, 11089 insertions, 6098 deletions
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java index 050fecde8213..d3938f4c0926 100644 --- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java +++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java @@ -17,7 +17,6 @@ package android.app; import android.content.Context; import android.content.res.Configuration; -import android.content.res.Resources; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; import android.view.Display; @@ -136,4 +135,22 @@ public class ResourcesManagerPerfTest { } } } + + @Test + public void getDisplayMetrics() { + ResourcesManager resourcesManager = ResourcesManager.getInstance(); + + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + state.pauseTiming(); + // Invalidate cache. + resourcesManager.applyConfigurationToResourcesLocked( + resourcesManager.getConfiguration(), null); + state.resumeTiming(); + + // Invoke twice for testing cache. + resourcesManager.getDisplayMetrics(); + resourcesManager.getDisplayMetrics(); + } + } } diff --git a/api/current.txt b/api/current.txt index c6af6beca60b..e5baa7c07cd5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11617,6 +11617,16 @@ package android.content.pm { field public int version; } + public final class FileChecksum implements android.os.Parcelable { + method public int describeContents(); + method public int getKind(); + method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException; + method @Nullable public String getSplitName(); + method @NonNull public byte[] getValue(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.FileChecksum> CREATOR; + } + public final class InstallSourceInfo implements android.os.Parcelable { method public int describeContents(); method @Nullable public String getInitiatingPackageName(); @@ -11992,6 +12002,7 @@ package android.content.pm { method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public CharSequence getBackgroundPermissionOptionLabel(); method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int); + method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException; method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName); method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon(); method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo); @@ -12082,6 +12093,7 @@ package android.content.pm { field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3 field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1 field public static final int DONT_KILL_APP = 1; // 0x1 + field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS"; field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID"; field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT"; field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays"; @@ -12236,6 +12248,8 @@ package android.content.pm { field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000 field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000 field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L + field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20 + field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40 field public static final int PERMISSION_DENIED = -1; // 0xffffffff field public static final int PERMISSION_GRANTED = 0; // 0x0 field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff @@ -12245,9 +12259,16 @@ package android.content.pm { field public static final int SIGNATURE_SECOND_NOT_SIGNED = -2; // 0xfffffffe field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc field public static final int SYNCHRONOUS = 2; // 0x2 + field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL; + field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE; field public static final int VERIFICATION_ALLOW = 1; // 0x1 field public static final int VERIFICATION_REJECT = -1; // 0xffffffff field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff + field public static final int WHOLE_MD5 = 2; // 0x2 + field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1 + field public static final int WHOLE_SHA1 = 4; // 0x4 + field public static final int WHOLE_SHA256 = 8; // 0x8 + field public static final int WHOLE_SHA512 = 16; // 0x10 } public static class PackageManager.NameNotFoundException extends android.util.AndroidException { diff --git a/api/system-current.txt b/api/system-current.txt index 41e859363581..00a59bf4f1e3 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -219,6 +219,7 @@ package android { field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT"; field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE"; field public static final String SHUTDOWN = "android.permission.SHUTDOWN"; + field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE"; field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES"; field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"; field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON"; diff --git a/api/test-current.txt b/api/test-current.txt index 529dcf71ef6e..5741fe7e90a4 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -5579,7 +5579,7 @@ package android.window { method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo); method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void registerOrganizer(); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static void setLaunchRoot(int, @NonNull android.window.WindowContainerToken); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void unregisterOrganizer(); } diff --git a/cmds/idmap2/tests/ResultTests.cpp b/cmds/idmap2/tests/ResultTests.cpp index cbced0ae32fb..f2f8854cec3a 100644 --- a/cmds/idmap2/tests/ResultTests.cpp +++ b/cmds/idmap2/tests/ResultTests.cpp @@ -260,7 +260,8 @@ TEST(ResultTests, CascadeError) { struct NoCopyContainer { uint32_t value; // NOLINT(misc-non-private-member-variables-in-classes) - DISALLOW_COPY_AND_ASSIGN(NoCopyContainer); + NoCopyContainer(const NoCopyContainer&) = delete; + NoCopyContainer& operator=(const NoCopyContainer&) = delete; }; Result<std::unique_ptr<NoCopyContainer>> CreateNoCopyContainer(bool succeed) { diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index 3b65f8225ee9..4574b2e34547 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -25,8 +25,9 @@ namespace statsd { using std::unordered_map; using std::vector; -CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index) - : ConditionTracker(id, index) { +CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index, + const uint64_t protoHash) + : ConditionTracker(id, index, protoHash) { VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId); } @@ -122,6 +123,49 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf return true; } +bool CombinationConditionTracker::onConfigUpdated( + const vector<Predicate>& allConditionProtos, const int index, + const vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, + const unordered_map<int64_t, int>& conditionTrackerMap) { + ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers, + atomMatchingTrackerMap, conditionTrackerMap); + mTrackerIndex.clear(); + mChildren.clear(); + mUnSlicedChildren.clear(); + mSlicedChildren.clear(); + Predicate_Combination combinationCondition = allConditionProtos[mIndex].combination(); + + for (const int64_t child : combinationCondition.predicate()) { + const auto& it = conditionTrackerMap.find(child); + + if (it == conditionTrackerMap.end()) { + ALOGW("Predicate %lld not found in the config", (long long)child); + return false; + } + + int childIndex = it->second; + const sp<ConditionTracker>& childTracker = allConditionTrackers[childIndex]; + + // Ensures that the child's tracker indices are updated. + if (!childTracker->onConfigUpdated(allConditionProtos, childIndex, allConditionTrackers, + atomMatchingTrackerMap, conditionTrackerMap)) { + ALOGW("Child update failed %lld ", (long long)child); + return false; + } + + if (allConditionTrackers[childIndex]->isSliced()) { + mSlicedChildren.push_back(childIndex); + } else { + mUnSlicedChildren.push_back(childIndex); + } + mChildren.push_back(childIndex); + mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(), + childTracker->getAtomMatchingTrackerIndex().end()); + } + return true; +} + void CombinationConditionTracker::isConditionMet( const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, const bool isPartialLink, diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index a7fac3deaabe..672d61c82268 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -24,9 +24,9 @@ namespace android { namespace os { namespace statsd { -class CombinationConditionTracker : public virtual ConditionTracker { +class CombinationConditionTracker : public ConditionTracker { public: - CombinationConditionTracker(const int64_t& id, const int index); + CombinationConditionTracker(const int64_t& id, const int index, const uint64_t protoHash); ~CombinationConditionTracker(); @@ -35,6 +35,11 @@ public: const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, std::vector<ConditionState>& conditionCache) override; + bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& conditionTrackerMap) override; + void evaluateCondition(const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues, const std::vector<sp<ConditionTracker>>& mAllConditions, @@ -102,6 +107,7 @@ private: std::vector<int> mSlicedChildren; std::vector<int> mUnSlicedChildren; + FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions); }; } // namespace statsd diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 4e1253506be7..3bf4e636be89 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -31,18 +31,17 @@ namespace statsd { class ConditionTracker : public virtual RefBase { public: - ConditionTracker(const int64_t& id, const int index) + ConditionTracker(const int64_t& id, const int index, const uint64_t protoHash) : mConditionId(id), mIndex(index), mInitialized(false), mTrackerIndex(), mUnSlicedPartCondition(ConditionState::kUnknown), - mSliced(false){}; + mSliced(false), + mProtoHash(protoHash){}; virtual ~ConditionTracker(){}; - inline const int64_t& getId() { return mConditionId; } - // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also // be done in the constructor, but we do it separately because (1) easy to return a bool to // indicate whether the initialization is successful. (2) makes unit test easier. @@ -50,7 +49,7 @@ public: // fill the condition cache with the current condition. // allConditionConfig: the list of all Predicate config from statsd_config. // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also - // need to call init() on children conditions) + // need to call init() on child conditions) // conditionIdIndexMap: the mapping from condition id to its index. // stack: a bit map to keep track which nodes have been visited on the stack in the recursion. // conditionCache: tracks initial conditions of all ConditionTrackers. returns the @@ -60,6 +59,26 @@ public: const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, std::vector<ConditionState>& conditionCache) = 0; + // Update appropriate state on config updates. Primarily, all indices need to be updated. + // This predicate and all of its children are guaranteed to be preserved across the update. + // This function is recursive and will call onConfigUpdated on child conditions. It does not + // manage cycle detection since all preserved conditions should not have any cycles. + // + // allConditionProtos: the new predicates. + // index: the new index of this tracker in allConditionProtos and allConditionTrackers. + // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also + // need to call onConfigUpdated() on child conditions) + // [atomMatchingTrackerMap]: map of atom matcher id to index after the config update + // [conditionTrackerMap]: map of condition tracker id to index after the config update. + // returns whether or not the update is successful + virtual bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& conditionTrackerMap) { + mIndex = index; + return true; + } + // evaluate current condition given the new event. // event: the new log event // eventMatcherValues: the results of the AtomMatchingTrackers. AtomMatchingTrackers always @@ -112,6 +131,10 @@ public: return mConditionId; } + inline uint64_t getProtoHash() const { + return mProtoHash; + } + virtual void getTrueSlicedDimensions( const std::vector<sp<ConditionTracker>>& allConditions, std::set<HashableDimensionKey>* dimensions) const = 0; @@ -133,7 +156,7 @@ protected: const int64_t mConditionId; // the index of this condition in the manager's condition list. - const int mIndex; + int mIndex; // if it's properly initialized. bool mInitialized; @@ -151,6 +174,12 @@ protected: ConditionState mUnSlicedPartCondition; bool mSliced; + + // Hash of the Predicate's proto bytes from StatsdConfig. + // Used to determine if the definition of this condition has changed across a config update. + const uint64_t mProtoHash; + + FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions); }; } // namespace statsd diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index f45759b6a77e..1dcc8f96131a 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -27,54 +27,21 @@ namespace statsd { using std::unordered_map; SimpleConditionTracker::SimpleConditionTracker( - const ConfigKey& key, const int64_t& id, const int index, + const ConfigKey& key, const int64_t& id, const uint64_t protoHash, const int index, const SimplePredicate& simplePredicate, - const unordered_map<int64_t, int>& trackerNameIndexMap) - : ConditionTracker(id, index), mConfigKey(key), mContainANYPositionInInternalDimensions(false) { + const unordered_map<int64_t, int>& atomMatchingTrackerMap) + : ConditionTracker(id, index, protoHash), + mConfigKey(key), + mContainANYPositionInInternalDimensions(false) { VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId); mCountNesting = simplePredicate.count_nesting(); - if (simplePredicate.has_start()) { - auto pair = trackerNameIndexMap.find(simplePredicate.start()); - if (pair == trackerNameIndexMap.end()) { - ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start()); - return; - } - mStartLogMatcherIndex = pair->second; - mTrackerIndex.insert(mStartLogMatcherIndex); - } else { - mStartLogMatcherIndex = -1; - } - - if (simplePredicate.has_stop()) { - auto pair = trackerNameIndexMap.find(simplePredicate.stop()); - if (pair == trackerNameIndexMap.end()) { - ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop()); - return; - } - mStopLogMatcherIndex = pair->second; - mTrackerIndex.insert(mStopLogMatcherIndex); - } else { - mStopLogMatcherIndex = -1; - } - - if (simplePredicate.has_stop_all()) { - auto pair = trackerNameIndexMap.find(simplePredicate.stop_all()); - if (pair == trackerNameIndexMap.end()) { - ALOGW("Stop all matcher %lld found in the config", (long long)simplePredicate.stop_all()); - return; - } - mStopAllLogMatcherIndex = pair->second; - mTrackerIndex.insert(mStopAllLogMatcherIndex); - } else { - mStopAllLogMatcherIndex = -1; - } + setMatcherIndices(simplePredicate, atomMatchingTrackerMap); if (simplePredicate.has_dimensions()) { translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions); if (mOutputDimensions.size() > 0) { mSliced = true; - mDimensionTag = mOutputDimensions[0].mMatcher.getTag(); } mContainANYPositionInInternalDimensions = HasPositionANY(simplePredicate.dimensions()); } @@ -106,6 +73,59 @@ bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig, return mInitialized; } +bool SimpleConditionTracker::onConfigUpdated( + const vector<Predicate>& allConditionProtos, const int index, + const vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, + const unordered_map<int64_t, int>& conditionTrackerMap) { + ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers, + atomMatchingTrackerMap, conditionTrackerMap); + setMatcherIndices(allConditionProtos[index].simple_predicate(), atomMatchingTrackerMap); + return true; +} + +void SimpleConditionTracker::setMatcherIndices( + const SimplePredicate& simplePredicate, + const unordered_map<int64_t, int>& atomMatchingTrackerMap) { + mTrackerIndex.clear(); + if (simplePredicate.has_start()) { + auto pair = atomMatchingTrackerMap.find(simplePredicate.start()); + if (pair == atomMatchingTrackerMap.end()) { + ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start()); + return; + } + mStartLogMatcherIndex = pair->second; + mTrackerIndex.insert(mStartLogMatcherIndex); + } else { + mStartLogMatcherIndex = -1; + } + + if (simplePredicate.has_stop()) { + auto pair = atomMatchingTrackerMap.find(simplePredicate.stop()); + if (pair == atomMatchingTrackerMap.end()) { + ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop()); + return; + } + mStopLogMatcherIndex = pair->second; + mTrackerIndex.insert(mStopLogMatcherIndex); + } else { + mStopLogMatcherIndex = -1; + } + + if (simplePredicate.has_stop_all()) { + auto pair = atomMatchingTrackerMap.find(simplePredicate.stop_all()); + if (pair == atomMatchingTrackerMap.end()) { + ALOGW("Stop all matcher %lld found in the config", + (long long)simplePredicate.stop_all()); + return; + } + mStopAllLogMatcherIndex = pair->second; + mTrackerIndex.insert(mStopAllLogMatcherIndex); + } else { + mStopAllLogMatcherIndex = -1; + } +} + void SimpleConditionTracker::dumpState() { VLOG("%lld DUMP:", (long long)mConditionId); for (const auto& pair : mSlicedConditionState) { diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index 1a9e35e38207..7a8b40108448 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -27,11 +27,11 @@ namespace android { namespace os { namespace statsd { -class SimpleConditionTracker : public virtual ConditionTracker { +class SimpleConditionTracker : public ConditionTracker { public: - SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const int index, - const SimplePredicate& simplePredicate, - const std::unordered_map<int64_t, int>& trackerNameIndexMap); + SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const uint64_t protoHash, + const int index, const SimplePredicate& simplePredicate, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap); ~SimpleConditionTracker(); @@ -40,6 +40,11 @@ public: const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, std::vector<ConditionState>& conditionCache) override; + bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& conditionTrackerMap) override; + void evaluateCondition(const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues, const std::vector<sp<ConditionTracker>>& mAllConditions, @@ -112,10 +117,11 @@ private: std::set<HashableDimensionKey> mLastChangedToTrueDimensions; std::set<HashableDimensionKey> mLastChangedToFalseDimensions; - int mDimensionTag; - std::map<HashableDimensionKey, int> mSlicedConditionState; + void setMatcherIndices(const SimplePredicate& predicate, + const std::unordered_map<int64_t, int>& logTrackerMap); + void handleStopAll(std::vector<ConditionState>& conditionCache, std::vector<bool>& changedCache); @@ -129,6 +135,7 @@ private: FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition); FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim); FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll); + FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 5a520326116a..a0c701ea4229 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -80,7 +80,7 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, mConfigValid = initStatsdConfig( key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap, - mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers, + mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap, mAlertTrackerMap, mMetricIndexesWithActivation, @@ -204,13 +204,20 @@ bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t time const sp<AlarmMonitor>& periodicAlarmMonitor) { vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers; unordered_map<int64_t, int> newAtomMatchingTrackerMap; + vector<sp<ConditionTracker>> newConditionTrackers; + unordered_map<int64_t, int> newConditionTrackerMap; mTagIds.clear(); + mTrackerToConditionMap.clear(); mConfigValid = updateStatsdConfig( mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap, mTagIds, - newAtomMatchingTrackers, newAtomMatchingTrackerMap); + timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap, + mAllConditionTrackers, mConditionTrackerMap, mTagIds, newAtomMatchingTrackers, + newAtomMatchingTrackerMap, newConditionTrackers, newConditionTrackerMap, + mTrackerToConditionMap); mAllAtomMatchingTrackers = newAtomMatchingTrackers; mAtomMatchingTrackerMap = newAtomMatchingTrackerMap; + mAllConditionTrackers = newConditionTrackers; + mConditionTrackerMap = newConditionTrackerMap; return mConfigValid; } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 6f4b2d7e9fa4..bd0c8161a884 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -242,9 +242,12 @@ private: // To make updating configs faster, we map the id of a AtomMatchingTracker, MetricProducer, and // ConditionTracker to its index in the corresponding vector. - // Maps the id of an atom matcher to its index in mAllAtomMatchingTrackers. + // Maps the id of an atom matching tracker to its index in mAllAtomMatchingTrackers. std::unordered_map<int64_t, int> mAtomMatchingTrackerMap; + // Maps the id of a condition tracker to its index in mAllConditionTrackers. + std::unordered_map<int64_t, int> mConditionTrackerMap; + // To make the log processing more efficient, we want to do as much filtering as possible // before we go into individual trackers and conditions to match. diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 9b684f1248c5..dcfbd5ea239b 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -411,7 +411,7 @@ void ValueMetricProducer::skipCurrentBucket(const int64_t dropTimeNs, void ValueMetricProducer::resetBase() { for (auto& slice : mCurrentBaseInfo) { - for (auto& baseInfo : slice.second) { + for (auto& baseInfo : slice.second.baseInfos) { baseInfo.hasBase = false; } } @@ -623,7 +623,7 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end(); if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) { auto it = mCurrentBaseInfo.find(whatKey); - for (auto& baseInfo : it->second) { + for (auto& baseInfo : it->second.baseInfos) { baseInfo.hasBase = false; } } @@ -652,7 +652,7 @@ void ValueMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { (unsigned long)mCurrentSlicedBucket.size()); if (verbose) { for (const auto& it : mCurrentSlicedBucket) { - for (const auto& interval : it.second) { + for (const auto& interval : it.second.intervals) { fprintf(out, "\t(what)%s\t(states)%s (value)%s\n", it.first.getDimensionKeyInWhat().toString().c_str(), it.first.getStateValuesKey().toString().c_str(), @@ -788,23 +788,23 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( return; } - vector<BaseInfo>& baseInfos = mCurrentBaseInfo[whatKey]; + DimensionsInWhatInfo& dimensionsInWhatInfo = mCurrentBaseInfo[whatKey]; + vector<BaseInfo>& baseInfos = dimensionsInWhatInfo.baseInfos; if (baseInfos.size() < mFieldMatchers.size()) { VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size()); baseInfos.resize(mFieldMatchers.size()); } - for (BaseInfo& baseInfo : baseInfos) { - if (!baseInfo.hasCurrentState) { - baseInfo.currentState = getUnknownStateKey(); - baseInfo.hasCurrentState = true; - } + if (!dimensionsInWhatInfo.hasCurrentState) { + dimensionsInWhatInfo.currentState = getUnknownStateKey(); + dimensionsInWhatInfo.hasCurrentState = true; } // We need to get the intervals stored with the previous state key so we can // close these value intervals. - const auto oldStateKey = baseInfos[0].currentState; - vector<Interval>& intervals = mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)]; + const auto oldStateKey = dimensionsInWhatInfo.currentState; + vector<Interval>& intervals = + mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)].intervals; if (intervals.size() < mFieldMatchers.size()) { VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size()); intervals.resize(mFieldMatchers.size()); @@ -818,14 +818,14 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( // Discussion here: http://ag/6124370. bool useAnomalyDetection = true; + dimensionsInWhatInfo.hasCurrentState = true; + dimensionsInWhatInfo.currentState = stateKey; for (int i = 0; i < (int)mFieldMatchers.size(); i++) { const Matcher& matcher = mFieldMatchers[i]; BaseInfo& baseInfo = baseInfos[i]; Interval& interval = intervals[i]; interval.valueIndex = i; Value value; - baseInfo.hasCurrentState = true; - baseInfo.currentState = stateKey; if (!getDoubleOrLong(event, matcher, value)) { VLOG("Failed to get value %d from event %s", i, event.ToString().c_str()); StatsdStats::getInstance().noteBadValueType(mMetricId); @@ -990,7 +990,7 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, bool bucketHasData = false; // The current bucket is large enough to keep. for (const auto& slice : mCurrentSlicedBucket) { - ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second); + PastValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second.intervals); bucket.mConditionTrueNs = conditionTrueDuration; // it will auto create new vector of ValuebucketInfo if the key is not found. if (bucket.valueIndex.size() > 0) { @@ -1030,9 +1030,9 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, mCurrentBucketNum += numBucketsForward; } -ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime, - const std::vector<Interval>& intervals) { - ValueBucket bucket; +PastValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime, + const std::vector<Interval>& intervals) { + PastValueBucket bucket; bucket.mBucketStartNs = mCurrentBucketStartTimeNs; bucket.mBucketEndNs = bucketEndTime; for (const auto& interval : intervals) { @@ -1059,7 +1059,7 @@ void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs) // Cleanup data structure to aggregate values. for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) { bool obsolete = true; - for (auto& interval : it->second) { + for (auto& interval : it->second.intervals) { interval.hasValue = false; interval.sampleSize = 0; if (interval.seenNewData) { @@ -1107,7 +1107,7 @@ void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) { continue; } // TODO: fix this when anomaly can accept double values - auto& interval = slice.second[0]; + auto& interval = slice.second.intervals[0]; if (interval.hasValue) { mCurrentFullBucket[slice.first] += interval.value.long_value; } @@ -1126,7 +1126,7 @@ void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) { for (auto& tracker : mAnomalyTrackers) { if (tracker != nullptr) { // TODO: fix this when anomaly can accept double values - auto& interval = slice.second[0]; + auto& interval = slice.second.intervals[0]; if (interval.hasValue) { tracker->addPastBucket(slice.first, interval.value.long_value, mCurrentBucketNum); @@ -1139,7 +1139,7 @@ void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) { // Accumulate partial bucket. for (const auto& slice : mCurrentSlicedBucket) { // TODO: fix this when anomaly can accept double values - auto& interval = slice.second[0]; + auto& interval = slice.second.intervals[0]; if (interval.hasValue) { mCurrentFullBucket[slice.first] += interval.value.long_value; } diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index e72002e88533..472cc33b97fa 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -31,7 +31,7 @@ namespace android { namespace os { namespace statsd { -struct ValueBucket { +struct PastValueBucket { int64_t mBucketStartNs; int64_t mBucketEndNs; std::vector<int> valueIndex; @@ -41,7 +41,6 @@ struct ValueBucket { int64_t mConditionTrueNs; }; - // Aggregates values within buckets. // // There are different events that might complete a bucket @@ -173,7 +172,7 @@ private: // if this is pulled metric const bool mIsPulled; - // internal state of an ongoing aggregation bucket. + // Tracks the value information of one value field. typedef struct { // Index in multi value aggregation. int valueIndex; @@ -188,25 +187,40 @@ private: bool seenNewData = false; } Interval; + // Internal state of an ongoing aggregation bucket. + typedef struct CurrentValueBucket { + // Value information for each value field of the metric. + std::vector<Interval> intervals; + } CurrentValueBucket; + + // Holds base information for diffing values from one value field. typedef struct { // Holds current base value of the dimension. Take diff and update if necessary. Value base; // Whether there is a base to diff to. bool hasBase; + } BaseInfo; + + // State key and base information for a specific DimensionsInWhat key. + typedef struct { + std::vector<BaseInfo> baseInfos; // Last seen state value(s). HashableDimensionKey currentState; // Whether this dimensions in what key has a current state key. bool hasCurrentState; - } BaseInfo; + } DimensionsInWhatInfo; - std::unordered_map<MetricDimensionKey, std::vector<Interval>> mCurrentSlicedBucket; + // Tracks the internal state in the ongoing aggregation bucket for each DimensionsInWhat + // key and StateValuesKey pair. + std::unordered_map<MetricDimensionKey, CurrentValueBucket> mCurrentSlicedBucket; - std::unordered_map<HashableDimensionKey, std::vector<BaseInfo>> mCurrentBaseInfo; + // Tracks current state key and base information for each DimensionsInWhat key. + std::unordered_map<HashableDimensionKey, DimensionsInWhatInfo> mCurrentBaseInfo; std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket; // Save the past buckets and we can clear when the StatsLogReport is dumped. - std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets; + std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>> mPastBuckets; const int64_t mMinBucketSizeNs; @@ -224,8 +238,8 @@ private: void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, int64_t originalPullTimeNs, int64_t eventElapsedTimeNs); - ValueBucket buildPartialBucket(int64_t bucketEndTime, - const std::vector<Interval>& intervals); + PastValueBucket buildPartialBucket(int64_t bucketEndTime, + const std::vector<Interval>& intervals); void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs); @@ -234,7 +248,7 @@ private: // Reset diff base and mHasGlobalBase void resetBase(); - static const size_t kBucketSize = sizeof(ValueBucket{}); + static const size_t kBucketSize = sizeof(PastValueBucket{}); const size_t mDimensionSoftLimit; diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp index 0983dc0b2c83..bd60b6bfcb8e 100644 --- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp @@ -15,6 +15,7 @@ */ #define DEBUG false // STOPSHIP if true +#include "Log.h" #include "config_update_utils.h" @@ -44,7 +45,7 @@ bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherI // Check if new matcher. const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id); if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) { - matchersToUpdate[matcherIdx] = UPDATE_REPLACE; + matchersToUpdate[matcherIdx] = UPDATE_NEW; return true; } @@ -103,11 +104,13 @@ bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherI return true; } -bool updateAtomTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - set<int>& allTagIds, unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers) { +bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + set<int>& allTagIds, + unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, + set<int64_t>& replacedMatchers) { const int atomMatcherCount = config.atom_matcher_size(); vector<AtomMatcher> matcherProtos; @@ -157,7 +160,10 @@ bool updateAtomTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, newAtomMatchingTrackers.push_back(tracker); break; } - case UPDATE_REPLACE: { + case UPDATE_REPLACE: + replacedMatchers.insert(id); + [[fallthrough]]; // Intentionally fallthrough to create the new matcher. + case UPDATE_NEW: { sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, i, uidMap); if (tracker == nullptr) { return false; @@ -187,6 +193,207 @@ bool updateAtomTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, return true; } +// Recursive function to determine if a condition needs to be updated. Populates conditionsToUpdate. +// Returns whether the function was successful or not. +bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx, + const unordered_map<int64_t, int>& oldConditionTrackerMap, + const vector<sp<ConditionTracker>>& oldConditionTrackers, + const unordered_map<int64_t, int>& newConditionTrackerMap, + const set<int64_t>& replacedMatchers, + vector<UpdateStatus>& conditionsToUpdate, + vector<bool>& cycleTracker) { + // Have already examined this condition. + if (conditionsToUpdate[conditionIdx] != UPDATE_UNKNOWN) { + return true; + } + + const Predicate& predicate = config.predicate(conditionIdx); + int64_t id = predicate.id(); + // Check if new condition. + const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id); + if (oldConditionTrackerIt == oldConditionTrackerMap.end()) { + conditionsToUpdate[conditionIdx] = UPDATE_NEW; + return true; + } + + // This is an existing condition. Check if it has changed. + string serializedCondition; + if (!predicate.SerializeToString(&serializedCondition)) { + ALOGE("Unable to serialize matcher %lld", (long long)id); + return false; + } + uint64_t newProtoHash = Hash64(serializedCondition); + if (newProtoHash != oldConditionTrackers[oldConditionTrackerIt->second]->getProtoHash()) { + conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; + return true; + } + + switch (predicate.contents_case()) { + case Predicate::ContentsCase::kSimplePredicate: { + // Need to check if any of the underlying matchers changed. + const SimplePredicate& simplePredicate = predicate.simple_predicate(); + if (simplePredicate.has_start()) { + if (replacedMatchers.find(simplePredicate.start()) != replacedMatchers.end()) { + conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; + return true; + } + } + if (simplePredicate.has_stop()) { + if (replacedMatchers.find(simplePredicate.stop()) != replacedMatchers.end()) { + conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; + return true; + } + } + if (simplePredicate.has_stop_all()) { + if (replacedMatchers.find(simplePredicate.stop_all()) != replacedMatchers.end()) { + conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; + return true; + } + } + conditionsToUpdate[conditionIdx] = UPDATE_PRESERVE; + return true; + } + case Predicate::ContentsCase::kCombination: { + // Need to recurse on the children to see if any of the child predicates changed. + cycleTracker[conditionIdx] = true; + UpdateStatus status = UPDATE_PRESERVE; + for (const int64_t childPredicateId : predicate.combination().predicate()) { + const auto& childIt = newConditionTrackerMap.find(childPredicateId); + if (childIt == newConditionTrackerMap.end()) { + ALOGW("Predicate %lld not found in the config", (long long)childPredicateId); + return false; + } + const int childIdx = childIt->second; + if (cycleTracker[childIdx]) { + ALOGE("Cycle detected in predicate config"); + return false; + } + if (!determineConditionUpdateStatus(config, childIdx, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, + cycleTracker)) { + return false; + } + + if (conditionsToUpdate[childIdx] == UPDATE_REPLACE) { + status = UPDATE_REPLACE; + break; + } + } + conditionsToUpdate[conditionIdx] = status; + cycleTracker[conditionIdx] = false; + return true; + } + default: { + ALOGE("Predicate \"%lld\" malformed", (long long)id); + return false; + } + } + + return true; +} + +bool updateConditions(const ConfigKey& key, const StatsdConfig& config, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, + const set<int64_t>& replacedMatchers, + const unordered_map<int64_t, int>& oldConditionTrackerMap, + const vector<sp<ConditionTracker>>& oldConditionTrackers, + unordered_map<int64_t, int>& newConditionTrackerMap, + vector<sp<ConditionTracker>>& newConditionTrackers, + unordered_map<int, vector<int>>& trackerToConditionMap, + vector<ConditionState>& conditionCache, set<int64_t>& replacedConditions) { + vector<Predicate> conditionProtos; + const int conditionTrackerCount = config.predicate_size(); + conditionProtos.reserve(conditionTrackerCount); + newConditionTrackers.reserve(conditionTrackerCount); + conditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated); + + for (int i = 0; i < conditionTrackerCount; i++) { + const Predicate& condition = config.predicate(i); + if (newConditionTrackerMap.find(condition.id()) != newConditionTrackerMap.end()) { + ALOGE("Duplicate Predicate found!"); + return false; + } + newConditionTrackerMap[condition.id()] = i; + conditionProtos.push_back(condition); + } + + vector<UpdateStatus> conditionsToUpdate(conditionTrackerCount, UPDATE_UNKNOWN); + vector<bool> cycleTracker(conditionTrackerCount, false); + for (int i = 0; i < conditionTrackerCount; i++) { + if (!determineConditionUpdateStatus(config, i, oldConditionTrackerMap, oldConditionTrackers, + newConditionTrackerMap, replacedMatchers, + conditionsToUpdate, cycleTracker)) { + return false; + } + } + + // Update status has been determined for all conditions. Now perform the update. + set<int> preservedConditions; + for (int i = 0; i < conditionTrackerCount; i++) { + const Predicate& predicate = config.predicate(i); + const int64_t id = predicate.id(); + switch (conditionsToUpdate[i]) { + case UPDATE_PRESERVE: { + preservedConditions.insert(i); + const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id); + if (oldConditionTrackerIt == oldConditionTrackerMap.end()) { + ALOGE("Could not find Predicate %lld in the previous config, but expected it " + "to be there", + (long long)id); + return false; + } + const int oldIndex = oldConditionTrackerIt->second; + newConditionTrackers.push_back(oldConditionTrackers[oldIndex]); + break; + } + case UPDATE_REPLACE: + replacedConditions.insert(id); + [[fallthrough]]; // Intentionally fallthrough to create the new condition tracker. + case UPDATE_NEW: { + sp<ConditionTracker> tracker = + createConditionTracker(key, predicate, i, atomMatchingTrackerMap); + if (tracker == nullptr) { + return false; + } + newConditionTrackers.push_back(tracker); + break; + } + default: { + ALOGE("Condition \"%lld\" update state is unknown. This should never happen", + (long long)id); + return false; + } + } + } + + // Update indices of preserved predicates. + for (const int conditionIndex : preservedConditions) { + if (!newConditionTrackers[conditionIndex]->onConfigUpdated( + conditionProtos, conditionIndex, newConditionTrackers, atomMatchingTrackerMap, + newConditionTrackerMap)) { + ALOGE("Failed to update condition %lld", + (long long)newConditionTrackers[conditionIndex]->getConditionId()); + return false; + } + } + + std::fill(cycleTracker.begin(), cycleTracker.end(), false); + for (int conditionIndex = 0; conditionIndex < conditionTrackerCount; conditionIndex++) { + const sp<ConditionTracker>& conditionTracker = newConditionTrackers[conditionIndex]; + // Calling init on preserved conditions is OK. It is needed to fill the condition cache. + if (!conditionTracker->init(conditionProtos, newConditionTrackers, newConditionTrackerMap, + cycleTracker, conditionCache)) { + return false; + } + for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) { + vector<int>& conditionList = trackerToConditionMap[trackerIndex]; + conditionList.push_back(conditionIndex); + } + } + return true; +} + bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor, @@ -194,14 +401,34 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const const int64_t currentTimeNs, const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const vector<sp<ConditionTracker>>& oldConditionTrackers, + const unordered_map<int64_t, int>& oldConditionTrackerMap, set<int>& allTagIds, vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, - unordered_map<int64_t, int>& newAtomMatchingTrackerMap) { - if (!updateAtomTrackers(config, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, - allTagIds, newAtomMatchingTrackerMap, newAtomMatchingTrackers)) { + unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + vector<sp<ConditionTracker>>& newConditionTrackers, + unordered_map<int64_t, int>& newConditionTrackerMap, + unordered_map<int, vector<int>>& trackerToConditionMap) { + set<int64_t> replacedMatchers; + set<int64_t> replacedConditions; + vector<ConditionState> conditionCache; + + if (!updateAtomMatchingTrackers(config, uidMap, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, allTagIds, newAtomMatchingTrackerMap, + newAtomMatchingTrackers, replacedMatchers)) { ALOGE("updateAtomMatchingTrackers failed"); return false; } + VLOG("updateAtomMatchingTrackers succeeded"); + + if (!updateConditions(key, config, newAtomMatchingTrackerMap, replacedMatchers, + oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap, + newConditionTrackers, trackerToConditionMap, conditionCache, + replacedConditions)) { + ALOGE("updateConditions failed"); + return false; + } + VLOG("updateConditions succeeded"); return true; } diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h index ae7b2162e034..7ba684a65e88 100644 --- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h +++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h @@ -19,6 +19,7 @@ #include <vector> #include "anomaly/AlarmMonitor.h" +#include "condition/ConditionTracker.h" #include "external/StatsPullerManager.h" #include "matchers/AtomMatchingTracker.h" @@ -31,30 +32,33 @@ namespace statsd { // All other functions are intermediate steps, created to make unit testing easier. // Possible update states for a component. PRESERVE means we should keep the existing one. -// REPLACE means we should create a new one, either because it didn't exist or it changed. +// REPLACE means we should create a new one because the existing one changed +// NEW means we should create a new one because one does not currently exist. enum UpdateStatus { UPDATE_UNKNOWN = 0, UPDATE_PRESERVE = 1, UPDATE_REPLACE = 2, + UPDATE_NEW = 3, }; // Recursive function to determine if a matcher needs to be updated. // input: // [config]: the input StatsdConfig // [matcherIdx]: the index of the current matcher to be updated -// [newAtomMatchingTrackerMap]: matcher id to index mapping in the input StatsdConfig // [oldAtomMatchingTrackerMap]: matcher id to index mapping in the existing MetricsManager // [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers +// [newAtomMatchingTrackerMap]: matcher id to index mapping in the input StatsdConfig // output: // [matchersToUpdate]: vector of the update status of each matcher. The matcherIdx index will // be updated from UPDATE_UNKNOWN after this call. // [cycleTracker]: intermediate param used during recursion. -bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherIdx, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - vector<UpdateStatus>& matchersToUpdate, - vector<bool>& cycleTracker); +// Returns whether the function was successful or not. +bool determineMatcherUpdateStatus( + const StatsdConfig& config, const int matcherIdx, + const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + std::vector<UpdateStatus>& matchersToUpdate, std::vector<bool>& cycleTracker); // Updates the AtomMatchingTrackers. // input: @@ -64,12 +68,61 @@ bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherI // output: // [allTagIds]: contains the set of all interesting tag ids to this config. // [newAtomMatchingTrackerMap]: new matcher id to index mapping -// [newAtomMatchers]: stores the new AtomMatchingTrackers -bool updateAtomTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, - const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - set<int>& allTagIds, unordered_map<int64_t, int>& newAtomMatchingTrackerMap, - vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers); +// [newAtomMatchingTrackers]: stores the new AtomMatchingTrackers +// [replacedMatchers]: set of matcher ids that changed and have been replaced +bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, + const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + std::set<int>& allTagIds, + std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, + std::set<int64_t>& replacedMatchers); + +// Recursive function to determine if a condition needs to be updated. +// input: +// [config]: the input StatsdConfig +// [conditionIdx]: the index of the current condition to be updated +// [oldConditionTrackerMap]: condition id to index mapping in the existing MetricsManager +// [oldConditionTrackers]: stores the existing ConditionTrackers +// [newConditionTrackerMap]: condition id to index mapping in the input StatsdConfig +// [replacedMatchers]: set of replaced matcher ids. conditions using these matchers must be replaced +// output: +// [conditionsToUpdate]: vector of the update status of each condition. The conditionIdx index will +// be updated from UPDATE_UNKNOWN after this call. +// [cycleTracker]: intermediate param used during recursion. +// Returns whether the function was successful or not. +bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx, + const std::unordered_map<int64_t, int>& oldConditionTrackerMap, + const std::vector<sp<ConditionTracker>>& oldConditionTrackers, + const std::unordered_map<int64_t, int>& newConditionTrackerMap, + const std::set<int64_t>& replacedMatchers, + std::vector<UpdateStatus>& conditionsToUpdate, + std::vector<bool>& cycleTracker); + +// Updates ConditionTrackers +// input: +// [config]: the input config +// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step. +// [replacedMatchers]: ids of replaced matchers. conditions depending on these must also be replaced +// [oldConditionTrackerMap]: existing matcher id to index mapping +// [oldConditionTrackers]: stores the existing ConditionTrackers +// output: +// [newConditionTrackerMap]: new condition id to index mapping +// [newConditionTrackers]: stores the sp to all the ConditionTrackers +// [trackerToConditionMap]: contains the mapping from the index of an atom matcher +// to indices of condition trackers that use the matcher +// [conditionCache]: stores the current conditions for each ConditionTracker +// [replacedConditions]: set of matcher ids that have changed and have been replaced +bool updateConditions(const ConfigKey& key, const StatsdConfig& config, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + const std::set<int64_t>& replacedMatchers, + const std::unordered_map<int64_t, int>& oldConditionTrackerMap, + const std::vector<sp<ConditionTracker>>& oldConditionTrackers, + std::unordered_map<int64_t, int>& newConditionTrackerMap, + std::vector<sp<ConditionTracker>>& newConditionTrackers, + std::unordered_map<int, std::vector<int>>& trackerToConditionMap, + std::vector<ConditionState>& conditionCache, + std::set<int64_t>& replacedConditions); // Updates the existing MetricsManager from a new StatsdConfig. // Parameters are the members of MetricsManager. See MetricsManager for declaration. @@ -79,10 +132,15 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, const int64_t currentTimeNs, const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, - const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const std::vector<sp<ConditionTracker>>& oldConditionTrackers, + const std::unordered_map<int64_t, int>& oldConditionTrackerMap, std::set<int>& allTagIds, std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, - unordered_map<int64_t, int>& newAtomMatchingTrackerMap); + std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + std::vector<sp<ConditionTracker>>& newConditionTrackers, + std::unordered_map<int64_t, int>& newConditionTrackerMap, + std::unordered_map<int, std::vector<int>>& trackerToConditionMap); } // namespace statsd } // namespace os 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 e40fbdb250f1..2e3e43413d54 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp @@ -74,16 +74,37 @@ sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, case AtomMatcher::ContentsCase::kSimpleAtomMatcher: return new SimpleAtomMatchingTracker(logMatcher.id(), index, protoHash, logMatcher.simple_atom_matcher(), uidMap); - break; case AtomMatcher::ContentsCase::kCombination: return new CombinationAtomMatchingTracker(logMatcher.id(), index, protoHash); - break; default: ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id()); return nullptr; } } +sp<ConditionTracker> createConditionTracker( + const ConfigKey& key, const Predicate& predicate, const int index, + const unordered_map<int64_t, int>& atomMatchingTrackerMap) { + string serializedPredicate; + if (!predicate.SerializeToString(&serializedPredicate)) { + ALOGE("Unable to serialize predicate %lld", (long long)predicate.id()); + return nullptr; + } + uint64_t protoHash = Hash64(serializedPredicate); + switch (predicate.contents_case()) { + case Predicate::ContentsCase::kSimplePredicate: { + return new SimpleConditionTracker(key, predicate.id(), protoHash, index, + predicate.simple_predicate(), atomMatchingTrackerMap); + } + case Predicate::ContentsCase::kCombination: { + return new CombinationConditionTracker(predicate.id(), index, protoHash); + } + default: + ALOGE("Predicate \"%lld\" malformed", (long long)predicate.id()); + return nullptr; + } +} + bool handleMetricWithAtomMatchingTrackers( const int64_t what, const int metricIndex, const bool usedForDimension, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, @@ -266,8 +287,7 @@ bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidM for (int i = 0; i < atomMatcherCount; i++) { const AtomMatcher& logMatcher = config.atom_matcher(i); - int index = allAtomMatchingTrackers.size(); - sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(logMatcher, index, uidMap); + sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(logMatcher, i, uidMap); if (tracker == nullptr) { return false; } @@ -276,7 +296,7 @@ bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidM ALOGE("Duplicate AtomMatcher found!"); return false; } - atomMatchingTrackerMap[logMatcher.id()] = index; + atomMatchingTrackerMap[logMatcher.id()] = i; matcherConfigs.push_back(logMatcher); } @@ -307,28 +327,17 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, for (int i = 0; i < conditionTrackerCount; i++) { const Predicate& condition = config.predicate(i); - int index = allConditionTrackers.size(); - switch (condition.contents_case()) { - case Predicate::ContentsCase::kSimplePredicate: { - allConditionTrackers.push_back(new SimpleConditionTracker( - key, condition.id(), index, condition.simple_predicate(), - atomMatchingTrackerMap)); - break; - } - case Predicate::ContentsCase::kCombination: { - allConditionTrackers.push_back( - new CombinationConditionTracker(condition.id(), index)); - break; - } - default: - ALOGE("Predicate \"%lld\" malformed", (long long)condition.id()); - return false; + sp<ConditionTracker> tracker = + createConditionTracker(key, condition, i, atomMatchingTrackerMap); + if (tracker == nullptr) { + return false; } + allConditionTrackers.push_back(tracker); if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) { ALOGE("Duplicate Predicate found!"); return false; } - conditionTrackerMap[condition.id()] = index; + conditionTrackerMap[condition.id()] = i; conditionConfigs.push_back(condition); } @@ -934,6 +943,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, unordered_map<int64_t, int>& atomMatchingTrackerMap, vector<sp<ConditionTracker>>& allConditionTrackers, + unordered_map<int64_t, int>& conditionTrackerMap, vector<sp<MetricProducer>>& allMetricProducers, vector<sp<AnomalyTracker>>& allAnomalyTrackers, vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers, @@ -944,7 +954,6 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, unordered_map<int64_t, int>& alertTrackerMap, vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds) { - unordered_map<int64_t, int> conditionTrackerMap; vector<ConditionState> initialConditionCache; unordered_map<int64_t, int> metricProducerMap; unordered_map<int64_t, int> stateAtomIdMap; diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h index 4cfd1b0465ea..6eabcf4971d3 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h @@ -42,6 +42,17 @@ namespace statsd { sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index, const sp<UidMap>& uidMap); +// Create a ConditionTracker. +// input: +// [predicate]: the input Predicate from the StatsdConfig +// [index]: the index of the condition tracker +// [atomMatchingTrackerMap]: map of atom matcher id to its index in allAtomMatchingTrackers +// output: +// new ConditionTracker, or null if the tracker is unable to be created +sp<ConditionTracker> createConditionTracker( + const ConfigKey& key, const Predicate& predicate, const int index, + const unordered_map<int64_t, int>& atomMatchingTrackerMap); + // Helper functions for MetricsManager to initialize from StatsdConfig. // *Note*: only initStatsdConfig() should be called from outside. // All other functions are intermediate @@ -77,7 +88,6 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, std::unordered_map<int64_t, int>& conditionTrackerMap, std::vector<sp<ConditionTracker>>& allConditionTrackers, std::unordered_map<int, std::vector<int>>& trackerToConditionMap, - std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks, std::vector<ConditionState>& initialConditionCache); // Initialize State maps using State protos in the config. These maps will @@ -111,7 +121,6 @@ bool initMetrics( const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, const std::unordered_map<int64_t, int>& conditionTrackerMap, - const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const unordered_map<int64_t, int>& stateAtomIdMap, const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, @@ -135,6 +144,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, std::unordered_map<int64_t, int>& atomMatchingTrackerMap, std::vector<sp<ConditionTracker>>& allConditionTrackers, + std::unordered_map<int64_t, int>& conditionTrackerMap, std::vector<sp<MetricProducer>>& allMetricProducers, vector<sp<AnomalyTracker>>& allAnomalyTrackers, vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers, diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index 07b5311b1207..8998b5f98df5 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -39,6 +39,7 @@ const ConfigKey kConfigKey(0, 12345); const int ATTRIBUTION_NODE_FIELD_ID = 1; const int ATTRIBUTION_UID_FIELD_ID = 1; const int TAG_ID = 1; +const uint64_t protoHash = 0x123456789; SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse, bool outputSlicedUid, Position position) { @@ -123,7 +124,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedInitialValueFalse) { trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), + SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash, 0 /*tracker index*/, simplePredicate, trackerNameIndexMap); @@ -177,7 +178,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedInitialValueUnknown) { trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), + SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash, 0 /*tracker index*/, simplePredicate, trackerNameIndexMap); @@ -231,8 +232,9 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), 0 /*tracker index*/, - simplePredicate, trackerNameIndexMap); + SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash, + 0 /*tracker index*/, simplePredicate, + trackerNameIndexMap); EXPECT_FALSE(conditionTracker.isSliced()); // This event is not accessed in this test besides dimensions which is why this is okay. @@ -317,7 +319,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), + SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash, 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); EXPECT_FALSE(conditionTracker.isSliced()); @@ -392,7 +394,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; - SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), + SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash, 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); @@ -514,7 +516,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; - SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), + SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash, 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); @@ -610,7 +612,7 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; - SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), + SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash, 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 1000aea14868..8790fe428d19 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -57,7 +57,7 @@ const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs; double epsilon = 0.001; static void assertPastBucketValuesSingleKey( - const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets, + const std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>>& mPastBuckets, const std::initializer_list<int>& expectedValuesList, const std::initializer_list<int64_t>& expectedDurationNsList, const std::initializer_list<int64_t>& expectedStartTimeNsList, @@ -79,7 +79,7 @@ static void assertPastBucketValuesSingleKey( ASSERT_EQ(1, mPastBuckets.size()); ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size()); - const vector<ValueBucket>& buckets = mPastBuckets.begin()->second; + const vector<PastValueBucket>& buckets = mPastBuckets.begin()->second; for (int i = 0; i < expectedValues.size(); i++) { EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value) << "Values differ at index " << i; @@ -288,8 +288,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(11, curBaseInfo.base.long_value); @@ -304,8 +305,8 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(23, curBaseInfo.base.long_value); @@ -322,8 +323,8 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(36, curBaseInfo.base.long_value); @@ -426,8 +427,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(11, curBaseInfo.base.long_value); @@ -455,8 +457,8 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; // the base was reset EXPECT_EQ(true, curBaseInfo.hasBase); @@ -489,8 +491,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(11, curBaseInfo.base.long_value); @@ -502,8 +505,8 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(10, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -516,8 +519,8 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(36, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -549,8 +552,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(11, curBaseInfo.base.long_value); @@ -562,8 +566,8 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(10, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -573,8 +577,8 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(36, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -624,8 +628,9 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; // startUpdated:false sum:0 start:100 EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(100, curBaseInfo.base.long_value); @@ -641,8 +646,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(110, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -654,8 +659,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(20, curInterval.value.long_value); EXPECT_EQ(false, curBaseInfo.hasBase); @@ -879,8 +884,9 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue); @@ -888,7 +894,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; EXPECT_EQ(30, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket2StartTimeNs); @@ -925,8 +931,8 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; EXPECT_EQ(20, curInterval.value.long_value); LogEvent event3(/*uid=*/0, /*pid=*/0); @@ -935,7 +941,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; EXPECT_EQ(50, curInterval.value.long_value); valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35); @@ -946,7 +952,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; EXPECT_EQ(50, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket2StartTimeNs); @@ -1089,8 +1095,9 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; // startUpdated:true sum:0 start:11 EXPECT_EQ(true, curBaseInfo.hasBase); @@ -1104,8 +1111,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; // tartUpdated:false sum:12 EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(23, curBaseInfo.base.long_value); @@ -1121,8 +1128,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; // startUpdated:false sum:12 EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(36, curBaseInfo.base.long_value); @@ -1180,8 +1187,9 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(100, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -1189,8 +1197,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { // pull on bucket boundary come late, condition change happens before it valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {bucketStartTimeNs}, {bucket2StartTimeNs}); EXPECT_EQ(false, curBaseInfo.hasBase); @@ -1203,8 +1211,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {bucketStartTimeNs}, {bucket2StartTimeNs}); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); } @@ -1252,8 +1260,9 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; // startUpdated:false sum:0 start:100 EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(100, curBaseInfo.base.long_value); @@ -1265,8 +1274,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {bucketStartTimeNs}, {bucket2StartTimeNs}); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); @@ -1274,8 +1283,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {bucketStartTimeNs}, {bucket2StartTimeNs}); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(130, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -1286,8 +1295,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(140, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); @@ -1327,7 +1336,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue); @@ -1335,7 +1344,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; EXPECT_EQ(10, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket2StartTimeNs); @@ -1364,7 +1373,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue); @@ -1374,7 +1383,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; EXPECT_EQ(20, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket2StartTimeNs); @@ -1405,7 +1414,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval; - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(1, curInterval.sampleSize); @@ -1414,7 +1423,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; EXPECT_EQ(25, curInterval.value.long_value); EXPECT_EQ(2, curInterval.sampleSize); @@ -1449,7 +1458,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue); @@ -1457,7 +1466,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; EXPECT_EQ(25, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket2StartTimeNs); @@ -1487,8 +1496,9 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(10, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -1499,7 +1509,7 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(5, curInterval.value.long_value); @@ -1509,8 +1519,8 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(15, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); @@ -1520,8 +1530,8 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(15, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); @@ -1558,12 +1568,13 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(10, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(20, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -1572,12 +1583,12 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { // has one slice ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(5, curInterval.value.long_value); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1]; EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(2, curInterval.value.long_value); @@ -1587,14 +1598,14 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(15, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(25, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); @@ -1604,13 +1615,13 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(15, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1]; + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(29, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); @@ -1656,9 +1667,9 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); auto iter = valueProducer->mCurrentSlicedBucket.begin(); - auto& interval1 = iter->second[0]; + auto& interval1 = iter->second.intervals[0]; auto iterBase = valueProducer->mCurrentBaseInfo.begin(); - auto& baseInfo1 = iterBase->second[0]; + auto& baseInfo1 = iterBase->second.baseInfos[0]; EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, baseInfo1.hasBase); EXPECT_EQ(3, baseInfo1.base.long_value); @@ -1692,8 +1703,8 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { } EXPECT_TRUE(it != iter); EXPECT_TRUE(itBase != iterBase); - auto& interval2 = it->second[0]; - auto& baseInfo2 = itBase->second[0]; + auto& interval2 = it->second.intervals[0]; + auto& baseInfo2 = itBase->second.baseInfos[0]; EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, baseInfo2.hasBase); EXPECT_EQ(4, baseInfo2.base.long_value); @@ -1732,9 +1743,10 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); const auto& it = valueProducer->mCurrentSlicedBucket.begin(); - ValueMetricProducer::Interval& interval1 = it->second[0]; + ValueMetricProducer::Interval& interval1 = it->second.intervals[0]; ValueMetricProducer::BaseInfo& baseInfo1 = - valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0]; + valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()) + ->second.baseInfos[0]; EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, baseInfo1.hasBase); EXPECT_EQ(3, baseInfo1.base.long_value); @@ -1761,9 +1773,10 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { } } EXPECT_TRUE(it2 != it); - ValueMetricProducer::Interval& interval2 = it2->second[0]; + ValueMetricProducer::Interval& interval2 = it2->second.intervals[0]; ValueMetricProducer::BaseInfo& baseInfo2 = - valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0]; + valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat()) + ->second.baseInfos[0]; EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, baseInfo2.hasBase); EXPECT_EQ(4, baseInfo2.base.long_value); @@ -1792,14 +1805,16 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { // Get new references now that entries have been deleted from the map const auto& it3 = valueProducer->mCurrentSlicedBucket.begin(); const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin()); - ASSERT_EQ(it3->second.size(), 1); - ASSERT_EQ(it4->second.size(), 1); - ValueMetricProducer::Interval& interval3 = it3->second[0]; - ValueMetricProducer::Interval& interval4 = it4->second[0]; + ASSERT_EQ(it3->second.intervals.size(), 1); + ASSERT_EQ(it4->second.intervals.size(), 1); + ValueMetricProducer::Interval& interval3 = it3->second.intervals[0]; + ValueMetricProducer::Interval& interval4 = it4->second.intervals[0]; ValueMetricProducer::BaseInfo& baseInfo3 = - valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())->second[0]; + valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat()) + ->second.baseInfos[0]; ValueMetricProducer::BaseInfo& baseInfo4 = - valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())->second[0]; + valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat()) + ->second.baseInfos[0]; EXPECT_EQ(true, baseInfo3.hasBase); EXPECT_EQ(5, baseInfo3.base.long_value); @@ -1837,9 +1852,9 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); auto iter = valueProducer->mCurrentSlicedBucket.begin(); - auto& interval1 = iter->second[0]; + auto& interval1 = iter->second.intervals[0]; auto iterBase = valueProducer->mCurrentBaseInfo.begin(); - auto& baseInfo1 = iterBase->second[0]; + auto& baseInfo1 = iterBase->second.baseInfos[0]; EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, baseInfo1.hasBase); EXPECT_EQ(3, baseInfo1.base.long_value); @@ -1875,8 +1890,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { } EXPECT_TRUE(it != iter); EXPECT_TRUE(itBase != iterBase); - auto interval2 = it->second[0]; - auto baseInfo2 = itBase->second[0]; + auto interval2 = it->second.intervals[0]; + auto baseInfo2 = itBase->second.baseInfos[0]; EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, baseInfo2.hasBase); EXPECT_EQ(4, baseInfo2.base.long_value); @@ -1889,8 +1904,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); // Only one interval left. One was trimmed. ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0]; + interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, baseInfo2.hasBase); EXPECT_EQ(5, baseInfo2.base.long_value); @@ -1903,8 +1918,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); - interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0]; + interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, baseInfo2.hasBase); EXPECT_EQ(14, baseInfo2.base.long_value); EXPECT_EQ(false, interval2.hasValue); @@ -1943,8 +1958,9 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfB // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo& curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(100, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -1980,8 +1996,9 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) { // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo& curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(100, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -2030,8 +2047,9 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { valueProducer->onConditionChanged(false, bucketStartTimeNs + 1); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(false, valueProducer->mHasGlobalBase); @@ -2103,8 +2121,9 @@ TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) { valueProducer->mHasGlobalBase = true; ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(100, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -2156,8 +2175,9 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed // Contains base from last pull which was successful. ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(140, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -2294,8 +2314,9 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) // Contains base from last pull which was successful. ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(140, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -2373,8 +2394,9 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) { // Last pull failed so base has been reset. ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(false, valueProducer->mHasGlobalBase); @@ -2460,8 +2482,9 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) { valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(true, valueProducer->mHasGlobalBase); @@ -2469,8 +2492,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) { // Empty pull. valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(false, valueProducer->mHasGlobalBase); @@ -2513,8 +2536,9 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { valueProducer->onConditionChanged(true, bucketStartTimeNs + 12); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(true, valueProducer->mHasGlobalBase); @@ -2524,8 +2548,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { allData.clear(); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; // Data is empty, base should be reset. EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(5, curBaseInfo.base.long_value); @@ -2570,14 +2594,14 @@ TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); auto iterator = valueProducer->mCurrentSlicedBucket.begin(); auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin(); - EXPECT_EQ(true, baseInfoIter->second[0].hasBase); - EXPECT_EQ(2, baseInfoIter->second[0].base.long_value); - EXPECT_EQ(false, iterator->second[0].hasValue); + EXPECT_EQ(true, baseInfoIter->second.baseInfos[0].hasBase); + EXPECT_EQ(2, baseInfoIter->second.baseInfos[0].base.long_value); + EXPECT_EQ(false, iterator->second.intervals[0].hasValue); iterator++; baseInfoIter++; - EXPECT_EQ(false, baseInfoIter->second[0].hasBase); - EXPECT_EQ(1, baseInfoIter->second[0].base.long_value); - EXPECT_EQ(false, iterator->second[0].hasValue); + EXPECT_EQ(false, baseInfoIter->second.baseInfos[0].hasBase); + EXPECT_EQ(1, baseInfoIter->second.baseInfos[0].base.long_value); + EXPECT_EQ(false, iterator->second.intervals[0].hasValue); EXPECT_EQ(true, valueProducer->mHasGlobalBase); } @@ -2676,8 +2700,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) { valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(5, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -2811,8 +2835,8 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { valueProducer->onConditionChanged(false, bucketStartTimeNs + 12); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(2, curInterval.value.long_value); @@ -3045,8 +3069,9 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges // has one slice ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(20, curInterval.value.long_value); @@ -3058,8 +3083,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8}, {bucketStartTimeNs}, {bucket2StartTimeNs}); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); } @@ -3091,8 +3116,9 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) { assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8}, {bucketStartTimeNs}, {bucket2StartTimeNs}); ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0]; + ValueMetricProducer::BaseInfo curBaseInfo = + valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0]; EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); } @@ -3984,18 +4010,18 @@ TEST(ValueMetricProducerTest, TestSlicedState) { // Base for dimension key {} auto it = valueProducer->mCurrentSlicedBucket.begin(); auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(3, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, kStateUnknown} EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Bucket status after screen state change kStateUnknown->ON. auto screenEvent = CreateScreenStateChangedEvent( @@ -4005,19 +4031,19 @@ TEST(ValueMetricProducerTest, TestSlicedState) { // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(5, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, kStateUnknown} EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(2, it->second.intervals[0].value.long_value); // Bucket status after screen state change ON->OFF. screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10, @@ -4027,26 +4053,26 @@ TEST(ValueMetricProducerTest, TestSlicedState) { // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(9, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, ON} EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(4, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(4, it->second.intervals[0].value.long_value); // Value for dimension, state key {{}, kStateUnknown} it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(2, it->second.intervals[0].value.long_value); // Bucket status after screen state change OFF->ON. screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15, @@ -4056,35 +4082,35 @@ TEST(ValueMetricProducerTest, TestSlicedState) { // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(21, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, OFF} EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(12, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(12, it->second.intervals[0].value.long_value); // Value for dimension, state key {{}, ON} it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(4, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(4, it->second.intervals[0].value.long_value); // Value for dimension, state key {{}, kStateUnknown} it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(2, it->second.intervals[0].value.long_value); // Start dump report and check output. ProtoOutputStream output; @@ -4195,18 +4221,18 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { // Base for dimension key {} auto it = valueProducer->mCurrentSlicedBucket.begin(); auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(3, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, {kStateUnknown}} EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Bucket status after screen state change kStateUnknown->ON. auto screenEvent = CreateScreenStateChangedEvent( @@ -4216,19 +4242,19 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(5, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(screenOnGroup.group_id(), - itBase->second[0].currentState.getValues()[0].mValue.long_value); + itBase->second.currentState.getValues()[0].mValue.long_value); // Value for dimension, state key {{}, kStateUnknown} EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(2, it->second.intervals[0].value.long_value); // Bucket status after screen state change ON->VR. // Both ON and VR are in the same state group, so the base should not change. @@ -4239,19 +4265,19 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(5, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(screenOnGroup.group_id(), - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, kStateUnknown} EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(2, it->second.intervals[0].value.long_value); // Bucket status after screen state change VR->ON. // Both ON and VR are in the same state group, so the base should not change. @@ -4262,19 +4288,19 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(5, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(screenOnGroup.group_id(), - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, kStateUnknown} EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(2, it->second.intervals[0].value.long_value); // Bucket status after screen state change VR->OFF. screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15, @@ -4284,27 +4310,27 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(21, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(screenOffGroup.group_id(), - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{}, ON GROUP} EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(screenOnGroup.group_id(), it->first.getStateValuesKey().getValues()[0].mValue.long_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(16, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(16, it->second.intervals[0].value.long_value); // Value for dimension, state key {{}, kStateUnknown} it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(2, it->second.intervals[0].value.long_value); // Start dump report and check output. ProtoOutputStream output; @@ -4447,35 +4473,35 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { // Base for dimension key {uid 1}. auto it = valueProducer->mCurrentSlicedBucket.begin(); auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(3, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{uid 1}, kStateUnknown} ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Base for dimension key {uid 2} it++; itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(7, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for dimension, state key {{uid 2}, kStateUnknown} ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Bucket status after uid 1 process state change kStateUnknown -> Foreground. auto uidProcessEvent = CreateUidProcessStateChangedEvent( @@ -4485,36 +4511,36 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { // Base for dimension key {uid 1}. it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(6, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for key {uid 1, kStateUnknown}. ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(3, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(3, it->second.intervals[0].value.long_value); // Base for dimension key {uid 2} it++; itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(7, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for key {uid 2, kStateUnknown} ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Bucket status after uid 2 process state change kStateUnknown -> Background. uidProcessEvent = CreateUidProcessStateChangedEvent( @@ -4524,36 +4550,36 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { // Base for dimension key {uid 1}. it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(6, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for key {uid 1, kStateUnknown}. ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(3, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(3, it->second.intervals[0].value.long_value); // Base for dimension key {uid 2} it++; itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(9, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for key {uid 2, kStateUnknown} ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(2, it->second.intervals[0].value.long_value); // Pull at end of first bucket. vector<shared_ptr<LogEvent>> allData; @@ -4570,36 +4596,36 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { it = valueProducer->mCurrentSlicedBucket.begin(); EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(15, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for key {uid 2, BACKGROUND}. ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Base for dimension key {uid 1} it++; itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(10, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(10, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for key {uid 1, kStateUnknown} ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* kStateTracker::kUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Value for key {uid 1, FOREGROUND} it++; @@ -4608,7 +4634,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Value for key {uid 2, kStateUnknown} it++; @@ -4617,7 +4643,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* kStateTracker::kUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Bucket status after uid 1 process state change from Foreground -> Background. uidProcessEvent = CreateUidProcessStateChangedEvent( @@ -4630,35 +4656,35 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { // Base for dimension key {uid 2}. it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(15, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for key {uid 2, BACKGROUND}. ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Base for dimension key {uid 1} it++; itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(13, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(13, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for key {uid 1, kStateUnknown} ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Value for key {uid 1, FOREGROUND} it++; ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); @@ -4666,8 +4692,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(3, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(3, it->second.intervals[0].value.long_value); // Value for key {uid 2, kStateUnknown} it++; ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); @@ -4675,7 +4701,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Bucket status after uid 1 process state change Background->Foreground. uidProcessEvent = CreateUidProcessStateChangedEvent( @@ -4687,36 +4713,36 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { // Base for dimension key {uid 2} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(15, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for key {uid 2, BACKGROUND} ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Base for dimension key {uid 1} it++; itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(17, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(17, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for key {uid 1, kStateUnknown} ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Value for key {uid 1, BACKGROUND} it++; @@ -4725,8 +4751,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(4, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(4, it->second.intervals[0].value.long_value); // Value for key {uid 1, FOREGROUND} it++; @@ -4735,8 +4761,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(3, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(3, it->second.intervals[0].value.long_value); // Value for key {uid 2, kStateUnknown} it++; @@ -4857,23 +4883,23 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { valueProducer->onConditionChanged(true, bucketStartTimeNs + 20 * NS_PER_SEC); // Base for dimension key {} ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - std::unordered_map<HashableDimensionKey, std::vector<ValueMetricProducer::BaseInfo>>::iterator + std::unordered_map<HashableDimensionKey, ValueMetricProducer::DimensionsInWhatInfo>::iterator itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(3, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(BatterySaverModeStateChanged::ON, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for key {{}, -1} ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - std::unordered_map<MetricDimensionKey, std::vector<ValueMetricProducer::Interval>>::iterator - it = valueProducer->mCurrentSlicedBucket.begin(); + std::unordered_map<MetricDimensionKey, ValueMetricProducer::CurrentValueBucket>::iterator it = + valueProducer->mCurrentSlicedBucket.begin(); EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /*StateTracker::kUnknown*/, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); + EXPECT_FALSE(it->second.intervals[0].hasValue); // Bucket status after battery saver mode OFF event. unique_ptr<LogEvent> batterySaverOffEvent = @@ -4882,12 +4908,12 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { // Base for dimension key {} ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(5, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(BatterySaverModeStateChanged::OFF, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for key {{}, ON} ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); it = valueProducer->mCurrentSlicedBucket.begin(); @@ -4895,8 +4921,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(BatterySaverModeStateChanged::ON, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(2, it->second.intervals[0].value.long_value); // Pull at end of first bucket. vector<shared_ptr<LogEvent>> allData; @@ -4909,23 +4935,23 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { // Base for dimension key {} ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(11, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(11, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(BatterySaverModeStateChanged::OFF, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Bucket 2 status after condition change to false. valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC); // Base for dimension key {} ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); - EXPECT_FALSE(itBase->second[0].hasBase); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); + EXPECT_FALSE(itBase->second.baseInfos[0].hasBase); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(BatterySaverModeStateChanged::OFF, - itBase->second[0].currentState.getValues()[0].mValue.int_value); + itBase->second.currentState.getValues()[0].mValue.int_value); // Value for key {{}, OFF} ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); it = valueProducer->mCurrentSlicedBucket.begin(); @@ -4933,8 +4959,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(BatterySaverModeStateChanged::OFF, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(4, it->second[0].value.long_value); + EXPECT_TRUE(it->second.intervals[0].hasValue); + EXPECT_EQ(4, it->second.intervals[0].value.long_value); // Start dump report and check output. ProtoOutputStream output; diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp index 8c698eb15d8d..890884bc5d83 100644 --- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp +++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp @@ -14,6 +14,7 @@ #include "src/metrics/parsing_utils/config_update_utils.h" +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <private/android_filesystem_config.h> #include <stdio.h> @@ -23,6 +24,8 @@ #include <vector> #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "src/condition/CombinationConditionTracker.h" +#include "src/condition/SimpleConditionTracker.h" #include "src/matchers/CombinationAtomMatchingTracker.h" #include "src/metrics/parsing_utils/metrics_manager_util.h" #include "tests/statsd_test_util.h" @@ -53,6 +56,7 @@ set<int> allTagIds; vector<sp<AtomMatchingTracker>> oldAtomMatchingTrackers; unordered_map<int64_t, int> oldAtomMatchingTrackerMap; vector<sp<ConditionTracker>> oldConditionTrackers; +unordered_map<int64_t, int> oldConditionTrackerMap; vector<sp<MetricProducer>> oldMetricProducers; std::vector<sp<AnomalyTracker>> oldAnomalyTrackers; std::vector<sp<AlarmTracker>> oldAlarmTrackers; @@ -75,6 +79,7 @@ public: oldAtomMatchingTrackers.clear(); oldAtomMatchingTrackerMap.clear(); oldConditionTrackers.clear(); + oldConditionTrackerMap.clear(); oldMetricProducers.clear(); oldAnomalyTrackers.clear(); oldAlarmTrackers.clear(); @@ -93,8 +98,8 @@ bool initConfig(const StatsdConfig& config) { return initStatsdConfig( key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, timeBaseNs, allTagIds, oldAtomMatchingTrackers, oldAtomMatchingTrackerMap, - oldConditionTrackers, oldMetricProducers, oldAnomalyTrackers, oldAlarmTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + oldConditionTrackers, oldConditionTrackerMap, oldMetricProducers, oldAnomalyTrackers, + oldAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, noReportMetricIds); } @@ -144,6 +149,30 @@ TEST_F(ConfigUpdateTest, TestSimpleMatcherReplace) { EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE); } +TEST_F(ConfigUpdateTest, TestSimpleMatcherNew) { + StatsdConfig config; + AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10); + *config.add_atom_matcher() = matcher; + + EXPECT_TRUE(initConfig(config)); + + StatsdConfig newConfig; + // Different id, so should be a new matcher. + AtomMatcher newMatcher = CreateSimpleAtomMatcher("DIFFERENT_NAME", /*atom=*/10); + int64_t matcherId = newMatcher.id(); + EXPECT_NE(matcherId, matcher.id()); + *newConfig.add_atom_matcher() = newMatcher; + + vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN); + vector<bool> cycleTracker(1, false); + unordered_map<int64_t, int> newAtomMatchingTrackerMap; + newAtomMatchingTrackerMap[matcherId] = 0; + EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newAtomMatchingTrackerMap, + matchersToUpdate, cycleTracker)); + EXPECT_EQ(matchersToUpdate[0], UPDATE_NEW); +} + TEST_F(ConfigUpdateTest, TestCombinationMatcherPreserve) { StatsdConfig config; AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10); @@ -338,9 +367,10 @@ TEST_F(ConfigUpdateTest, TestUpdateMatchers) { set<int> newTagIds; unordered_map<int64_t, int> newAtomMatchingTrackerMap; vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers; - EXPECT_TRUE(updateAtomTrackers(newConfig, uidMap, oldAtomMatchingTrackerMap, - oldAtomMatchingTrackers, newTagIds, newAtomMatchingTrackerMap, - newAtomMatchingTrackers)); + set<int64_t> replacedMatchers; + EXPECT_TRUE(updateAtomMatchingTrackers( + newConfig, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, newTagIds, + newAtomMatchingTrackerMap, newAtomMatchingTrackers, replacedMatchers)); ASSERT_EQ(newTagIds.size(), 3); EXPECT_EQ(newTagIds.count(10), 1); @@ -405,8 +435,454 @@ TEST_F(ConfigUpdateTest, TestUpdateMatchers) { EXPECT_EQ(childMatchers->size(), 2); EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end()); EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 4), childMatchers->end()); + + // Expect replacedMatchers to have simple2 and combination2 + ASSERT_EQ(replacedMatchers.size(), 2); + EXPECT_NE(replacedMatchers.find(simple2Id), replacedMatchers.end()); + EXPECT_NE(replacedMatchers.find(combination2Id), replacedMatchers.end()); +} + +TEST_F(ConfigUpdateTest, TestSimpleConditionPreserve) { + StatsdConfig config; + AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = startMatcher; + AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = stopMatcher; + + Predicate predicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = predicate; + + // Create an initial config. + EXPECT_TRUE(initConfig(config)); + + set<int64_t> replacedMatchers; + vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN); + vector<bool> cycleTracker(1, false); + unordered_map<int64_t, int> newConditionTrackerMap; + newConditionTrackerMap[predicate.id()] = 0; + EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, cycleTracker)); + EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE); +} + +TEST_F(ConfigUpdateTest, TestSimpleConditionReplace) { + StatsdConfig config; + AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = startMatcher; + AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = stopMatcher; + + Predicate predicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = predicate; + + EXPECT_TRUE(initConfig(config)); + + // Modify the predicate. + config.mutable_predicate(0)->mutable_simple_predicate()->set_count_nesting(true); + + set<int64_t> replacedMatchers; + vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN); + vector<bool> cycleTracker(1, false); + unordered_map<int64_t, int> newConditionTrackerMap; + newConditionTrackerMap[predicate.id()] = 0; + EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, cycleTracker)); + EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE); +} + +TEST_F(ConfigUpdateTest, TestSimpleConditionDepsChange) { + StatsdConfig config; + AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher(); + int64_t startMatcherId = startMatcher.id(); + *config.add_atom_matcher() = startMatcher; + AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = stopMatcher; + + Predicate predicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = predicate; + + EXPECT_TRUE(initConfig(config)); + + // Start matcher was replaced. + set<int64_t> replacedMatchers; + replacedMatchers.insert(startMatcherId); + + vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN); + vector<bool> cycleTracker(1, false); + unordered_map<int64_t, int> newConditionTrackerMap; + newConditionTrackerMap[predicate.id()] = 0; + EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, cycleTracker)); + EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE); +} + +TEST_F(ConfigUpdateTest, TestCombinationConditionPreserve) { + StatsdConfig config; + AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = screenOnMatcher; + AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = screenOffMatcher; + + Predicate simple1 = CreateScreenIsOnPredicate(); + *config.add_predicate() = simple1; + Predicate simple2 = CreateScreenIsOffPredicate(); + *config.add_predicate() = simple2; + + Predicate combination1; + combination1.set_id(StringToId("COMBINATION1")); + Predicate_Combination* combinationInternal = combination1.mutable_combination(); + combinationInternal->set_operation(LogicalOperation::NAND); + combinationInternal->add_predicate(simple1.id()); + combinationInternal->add_predicate(simple2.id()); + *config.add_predicate() = combination1; + + EXPECT_TRUE(initConfig(config)); + + // Same predicates, different order + StatsdConfig newConfig; + unordered_map<int64_t, int> newConditionTrackerMap; + *newConfig.add_predicate() = combination1; + newConditionTrackerMap[combination1.id()] = 0; + *newConfig.add_predicate() = simple2; + newConditionTrackerMap[simple2.id()] = 1; + *newConfig.add_predicate() = simple1; + newConditionTrackerMap[simple1.id()] = 2; + + set<int64_t> replacedMatchers; + vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN); + vector<bool> cycleTracker(3, false); + // Only update the combination. It should recurse the two child predicates and preserve all 3. + EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, cycleTracker)); + EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE); + EXPECT_EQ(conditionsToUpdate[1], UPDATE_PRESERVE); + EXPECT_EQ(conditionsToUpdate[2], UPDATE_PRESERVE); } +TEST_F(ConfigUpdateTest, TestCombinationConditionReplace) { + StatsdConfig config; + AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = screenOnMatcher; + AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = screenOffMatcher; + + Predicate simple1 = CreateScreenIsOnPredicate(); + *config.add_predicate() = simple1; + Predicate simple2 = CreateScreenIsOffPredicate(); + *config.add_predicate() = simple2; + + Predicate combination1; + combination1.set_id(StringToId("COMBINATION1")); + Predicate_Combination* combinationInternal = combination1.mutable_combination(); + combinationInternal->set_operation(LogicalOperation::NAND); + combinationInternal->add_predicate(simple1.id()); + combinationInternal->add_predicate(simple2.id()); + *config.add_predicate() = combination1; + + EXPECT_TRUE(initConfig(config)); + + // Changing the logical operation changes the predicate definition, so it should be replaced. + combination1.mutable_combination()->set_operation(LogicalOperation::OR); + + StatsdConfig newConfig; + unordered_map<int64_t, int> newConditionTrackerMap; + *newConfig.add_predicate() = combination1; + newConditionTrackerMap[combination1.id()] = 0; + *newConfig.add_predicate() = simple2; + newConditionTrackerMap[simple2.id()] = 1; + *newConfig.add_predicate() = simple1; + newConditionTrackerMap[simple1.id()] = 2; + + set<int64_t> replacedMatchers; + vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN); + vector<bool> cycleTracker(3, false); + // Only update the combination. The simple conditions should not be evaluated. + EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, cycleTracker)); + EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE); + EXPECT_EQ(conditionsToUpdate[1], UPDATE_UNKNOWN); + EXPECT_EQ(conditionsToUpdate[2], UPDATE_UNKNOWN); +} + +TEST_F(ConfigUpdateTest, TestCombinationConditionDepsChange) { + StatsdConfig config; + AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = screenOnMatcher; + AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = screenOffMatcher; + + Predicate simple1 = CreateScreenIsOnPredicate(); + *config.add_predicate() = simple1; + Predicate simple2 = CreateScreenIsOffPredicate(); + *config.add_predicate() = simple2; + + Predicate combination1; + combination1.set_id(StringToId("COMBINATION1")); + Predicate_Combination* combinationInternal = combination1.mutable_combination(); + combinationInternal->set_operation(LogicalOperation::NAND); + combinationInternal->add_predicate(simple1.id()); + combinationInternal->add_predicate(simple2.id()); + *config.add_predicate() = combination1; + + EXPECT_TRUE(initConfig(config)); + + simple2.mutable_simple_predicate()->set_count_nesting(false); + + StatsdConfig newConfig; + unordered_map<int64_t, int> newConditionTrackerMap; + *newConfig.add_predicate() = combination1; + newConditionTrackerMap[combination1.id()] = 0; + *newConfig.add_predicate() = simple2; + newConditionTrackerMap[simple2.id()] = 1; + *newConfig.add_predicate() = simple1; + newConditionTrackerMap[simple1.id()] = 2; + + set<int64_t> replacedMatchers; + vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN); + vector<bool> cycleTracker(3, false); + // Only update the combination. Simple2 and combination1 must be evaluated. + EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap, + oldConditionTrackers, newConditionTrackerMap, + replacedMatchers, conditionsToUpdate, cycleTracker)); + EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE); + EXPECT_EQ(conditionsToUpdate[1], UPDATE_REPLACE); +} + +TEST_F(ConfigUpdateTest, TestUpdateConditions) { + StatsdConfig config; + + // Add atom matchers. These are mostly needed for initStatsdConfig + AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); + int64_t matcher1Id = matcher1.id(); + *config.add_atom_matcher() = matcher1; + + AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); + int64_t matcher2Id = matcher2.id(); + *config.add_atom_matcher() = matcher2; + + AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher(); + int64_t matcher3Id = matcher3.id(); + *config.add_atom_matcher() = matcher3; + + AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher(); + int64_t matcher4Id = matcher4.id(); + *config.add_atom_matcher() = matcher4; + + AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher(); + int64_t matcher5Id = matcher5.id(); + *config.add_atom_matcher() = matcher5; + + AtomMatcher matcher6 = CreateBatterySaverModeStopAtomMatcher(); + int64_t matcher6Id = matcher6.id(); + *config.add_atom_matcher() = matcher6; + + // Add the predicates. + // Will be preserved. + Predicate simple1 = CreateScreenIsOnPredicate(); + int64_t simple1Id = simple1.id(); + *config.add_predicate() = simple1; + + // Will be preserved. + Predicate simple2 = CreateScheduledJobPredicate(); + int64_t simple2Id = simple2.id(); + *config.add_predicate() = simple2; + + // Will be replaced. + Predicate simple3 = CreateBatterySaverModePredicate(); + int64_t simple3Id = simple3.id(); + *config.add_predicate() = simple3; + + // Will be preserved + Predicate combination1; + combination1.set_id(StringToId("COMBINATION1")); + combination1.mutable_combination()->set_operation(LogicalOperation::AND); + combination1.mutable_combination()->add_predicate(simple1Id); + combination1.mutable_combination()->add_predicate(simple2Id); + int64_t combination1Id = combination1.id(); + *config.add_predicate() = combination1; + + // Will be replaced since simple3 will be replaced. + Predicate combination2; + combination2.set_id(StringToId("COMBINATION2")); + combination2.mutable_combination()->set_operation(LogicalOperation::OR); + combination2.mutable_combination()->add_predicate(simple1Id); + combination2.mutable_combination()->add_predicate(simple3Id); + int64_t combination2Id = combination2.id(); + *config.add_predicate() = combination2; + + // Will be removed. + Predicate combination3; + combination3.set_id(StringToId("COMBINATION3")); + combination3.mutable_combination()->set_operation(LogicalOperation::NOT); + combination3.mutable_combination()->add_predicate(simple2Id); + int64_t combination3Id = combination3.id(); + *config.add_predicate() = combination3; + + EXPECT_TRUE(initConfig(config)); + + // Mark marcher 5 as replaced. Causes simple3, and therefore combination2 to be replaced. + set<int64_t> replacedMatchers; + replacedMatchers.insert(matcher6Id); + + // Change the condition of simple1 to true. + ASSERT_EQ(oldConditionTrackers[0]->getConditionId(), simple1Id); + LogEvent event(/*uid=*/0, /*pid=*/0); // Empty event is fine since there are no dimensions. + // Mark the stop matcher as matched, condition should be false. + vector<MatchingState> eventMatcherValues(6, MatchingState::kNotMatched); + eventMatcherValues[1] = MatchingState::kMatched; + vector<ConditionState> tmpConditionCache(6, ConditionState::kNotEvaluated); + vector<bool> conditionChangeCache(6, false); + oldConditionTrackers[0]->evaluateCondition(event, eventMatcherValues, oldConditionTrackers, + tmpConditionCache, conditionChangeCache); + EXPECT_EQ(tmpConditionCache[0], ConditionState::kFalse); + EXPECT_EQ(conditionChangeCache[0], true); + + // New combination matcher. Should have an initial condition of true since it is NOT(simple1). + Predicate combination4; + combination4.set_id(StringToId("COMBINATION4")); + combination4.mutable_combination()->set_operation(LogicalOperation::NOT); + combination4.mutable_combination()->add_predicate(simple1Id); + int64_t combination4Id = combination4.id(); + *config.add_predicate() = combination4; + + // Map the matchers in reverse order to force the indices to change. + std::unordered_map<int64_t, int> newAtomMatchingTrackerMap; + const int matcher6Index = 0; + newAtomMatchingTrackerMap[matcher6Id] = 0; + const int matcher5Index = 1; + newAtomMatchingTrackerMap[matcher5Id] = 1; + const int matcher4Index = 2; + newAtomMatchingTrackerMap[matcher4Id] = 2; + const int matcher3Index = 3; + newAtomMatchingTrackerMap[matcher3Id] = 3; + const int matcher2Index = 4; + newAtomMatchingTrackerMap[matcher2Id] = 4; + const int matcher1Index = 5; + newAtomMatchingTrackerMap[matcher1Id] = 5; + + StatsdConfig newConfig; + *newConfig.add_predicate() = simple3; + const int simple3Index = 0; + *newConfig.add_predicate() = combination2; + const int combination2Index = 1; + *newConfig.add_predicate() = combination4; + const int combination4Index = 2; + *newConfig.add_predicate() = simple2; + const int simple2Index = 3; + *newConfig.add_predicate() = combination1; + const int combination1Index = 4; + *newConfig.add_predicate() = simple1; + const int simple1Index = 5; + + unordered_map<int64_t, int> newConditionTrackerMap; + vector<sp<ConditionTracker>> newConditionTrackers; + unordered_map<int, vector<int>> trackerToConditionMap; + std::vector<ConditionState> conditionCache; + std::set<int64_t> replacedConditions; + EXPECT_TRUE(updateConditions(key, newConfig, newAtomMatchingTrackerMap, replacedMatchers, + oldConditionTrackerMap, oldConditionTrackers, + newConditionTrackerMap, newConditionTrackers, + trackerToConditionMap, conditionCache, replacedConditions)); + + unordered_map<int64_t, int> expectedConditionTrackerMap = { + {simple1Id, simple1Index}, {simple2Id, simple2Index}, + {simple3Id, simple3Index}, {combination1Id, combination1Index}, + {combination2Id, combination2Index}, {combination4Id, combination4Index}, + }; + EXPECT_THAT(newConditionTrackerMap, ContainerEq(expectedConditionTrackerMap)); + + ASSERT_EQ(newConditionTrackers.size(), 6); + // Make sure all conditions are initialized: + for (const sp<ConditionTracker>& tracker : newConditionTrackers) { + EXPECT_TRUE(tracker->mInitialized); + } + + // Make sure preserved conditions are the same. + EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple1Id)], + newConditionTrackers[newConditionTrackerMap.at(simple1Id)]); + EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple2Id)], + newConditionTrackers[newConditionTrackerMap.at(simple2Id)]); + EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(combination1Id)], + newConditionTrackers[newConditionTrackerMap.at(combination1Id)]); + + // Make sure replaced conditions are different and included in replacedConditions. + EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(simple3Id)], + newConditionTrackers[newConditionTrackerMap.at(simple3Id)]); + EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(combination2Id)], + newConditionTrackers[newConditionTrackerMap.at(combination2Id)]); + EXPECT_THAT(replacedConditions, ContainerEq(set({simple3Id, combination2Id}))); + + // Verify the trackerToConditionMap + ASSERT_EQ(trackerToConditionMap.size(), 6); + const vector<int>& matcher1Conditions = trackerToConditionMap[matcher1Index]; + EXPECT_THAT(matcher1Conditions, UnorderedElementsAre(simple1Index, combination1Index, + combination2Index, combination4Index)); + const vector<int>& matcher2Conditions = trackerToConditionMap[matcher2Index]; + EXPECT_THAT(matcher2Conditions, UnorderedElementsAre(simple1Index, combination1Index, + combination2Index, combination4Index)); + const vector<int>& matcher3Conditions = trackerToConditionMap[matcher3Index]; + EXPECT_THAT(matcher3Conditions, UnorderedElementsAre(simple2Index, combination1Index)); + const vector<int>& matcher4Conditions = trackerToConditionMap[matcher4Index]; + EXPECT_THAT(matcher4Conditions, UnorderedElementsAre(simple2Index, combination1Index)); + const vector<int>& matcher5Conditions = trackerToConditionMap[matcher5Index]; + EXPECT_THAT(matcher5Conditions, UnorderedElementsAre(simple3Index, combination2Index)); + const vector<int>& matcher6Conditions = trackerToConditionMap[matcher6Index]; + EXPECT_THAT(matcher6Conditions, UnorderedElementsAre(simple3Index, combination2Index)); + + // Verify the conditionCache. Specifically, simple1 is false and combination4 is true. + ASSERT_EQ(conditionCache.size(), 6); + EXPECT_EQ(conditionCache[simple1Index], ConditionState::kFalse); + EXPECT_EQ(conditionCache[simple2Index], ConditionState::kUnknown); + EXPECT_EQ(conditionCache[simple3Index], ConditionState::kUnknown); + EXPECT_EQ(conditionCache[combination1Index], ConditionState::kUnknown); + EXPECT_EQ(conditionCache[combination2Index], ConditionState::kUnknown); + EXPECT_EQ(conditionCache[combination4Index], ConditionState::kTrue); + + // Verify tracker indices/ids are correct. + EXPECT_EQ(newConditionTrackers[simple1Index]->getConditionId(), simple1Id); + EXPECT_EQ(newConditionTrackers[simple1Index]->mIndex, simple1Index); + EXPECT_TRUE(newConditionTrackers[simple1Index]->IsSimpleCondition()); + EXPECT_EQ(newConditionTrackers[simple2Index]->getConditionId(), simple2Id); + EXPECT_EQ(newConditionTrackers[simple2Index]->mIndex, simple2Index); + EXPECT_TRUE(newConditionTrackers[simple2Index]->IsSimpleCondition()); + EXPECT_EQ(newConditionTrackers[simple3Index]->getConditionId(), simple3Id); + EXPECT_EQ(newConditionTrackers[simple3Index]->mIndex, simple3Index); + EXPECT_TRUE(newConditionTrackers[simple3Index]->IsSimpleCondition()); + EXPECT_EQ(newConditionTrackers[combination1Index]->getConditionId(), combination1Id); + EXPECT_EQ(newConditionTrackers[combination1Index]->mIndex, combination1Index); + EXPECT_FALSE(newConditionTrackers[combination1Index]->IsSimpleCondition()); + EXPECT_EQ(newConditionTrackers[combination2Index]->getConditionId(), combination2Id); + EXPECT_EQ(newConditionTrackers[combination2Index]->mIndex, combination2Index); + EXPECT_FALSE(newConditionTrackers[combination2Index]->IsSimpleCondition()); + EXPECT_EQ(newConditionTrackers[combination4Index]->getConditionId(), combination4Id); + EXPECT_EQ(newConditionTrackers[combination4Index]->mIndex, combination4Index); + EXPECT_FALSE(newConditionTrackers[combination4Index]->IsSimpleCondition()); + + // Verify preserved trackers have indices updated. + SimpleConditionTracker* simpleTracker1 = + static_cast<SimpleConditionTracker*>(newConditionTrackers[simple1Index].get()); + EXPECT_EQ(simpleTracker1->mStartLogMatcherIndex, matcher1Index); + EXPECT_EQ(simpleTracker1->mStopLogMatcherIndex, matcher2Index); + EXPECT_EQ(simpleTracker1->mStopAllLogMatcherIndex, -1); + + SimpleConditionTracker* simpleTracker2 = + static_cast<SimpleConditionTracker*>(newConditionTrackers[simple2Index].get()); + EXPECT_EQ(simpleTracker2->mStartLogMatcherIndex, matcher3Index); + EXPECT_EQ(simpleTracker2->mStopLogMatcherIndex, matcher4Index); + EXPECT_EQ(simpleTracker2->mStopAllLogMatcherIndex, -1); + + CombinationConditionTracker* combinationTracker1 = static_cast<CombinationConditionTracker*>( + newConditionTrackers[combination1Index].get()); + EXPECT_THAT(combinationTracker1->mChildren, UnorderedElementsAre(simple1Index, simple2Index)); + EXPECT_THAT(combinationTracker1->mUnSlicedChildren, + UnorderedElementsAre(simple1Index, simple2Index)); + EXPECT_THAT(combinationTracker1->mSlicedChildren, IsEmpty()); +} } // namespace statsd } // namespace os } // namespace android 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 d6db4c12ae4d..e6583c9686ec 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 @@ -384,8 +384,9 @@ TEST(MetricsManagerTest, TestInitialConditions) { StatsdConfig config = buildConfigWithDifferentPredicates(); set<int> allTagIds; vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> logTrackerMap; + unordered_map<int64_t, int> atomMatchingTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; + unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; @@ -400,9 +401,9 @@ TEST(MetricsManagerTest, TestInitialConditions) { EXPECT_TRUE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, - allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, + allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, noReportMetricIds)); ASSERT_EQ(4u, allMetricProducers.size()); @@ -433,8 +434,9 @@ TEST(MetricsManagerTest, TestGoodConfig) { StatsdConfig config = buildGoodConfig(); set<int> allTagIds; vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> logTrackerMap; + unordered_map<int64_t, int> atomMatchingTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; + unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; @@ -449,9 +451,9 @@ TEST(MetricsManagerTest, TestGoodConfig) { EXPECT_TRUE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, - allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, + allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, noReportMetricIds)); ASSERT_EQ(1u, allMetricProducers.size()); @@ -470,8 +472,9 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { StatsdConfig config = buildDimensionMetricsWithMultiTags(); set<int> allTagIds; vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> logTrackerMap; + unordered_map<int64_t, int> atomMatchingTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; + unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; @@ -486,9 +489,9 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, - allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, + allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, noReportMetricIds)); } @@ -501,8 +504,9 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { StatsdConfig config = buildCircleMatchers(); set<int> allTagIds; vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> logTrackerMap; + unordered_map<int64_t, int> atomMatchingTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; + unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; @@ -517,9 +521,9 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, - allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, + allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, noReportMetricIds)); } @@ -532,8 +536,9 @@ TEST(MetricsManagerTest, TestMissingMatchers) { StatsdConfig config = buildMissingMatchers(); set<int> allTagIds; vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> logTrackerMap; + unordered_map<int64_t, int> atomMatchingTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; + unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; @@ -547,9 +552,9 @@ TEST(MetricsManagerTest, TestMissingMatchers) { std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, - allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, + allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, noReportMetricIds)); } @@ -562,8 +567,9 @@ TEST(MetricsManagerTest, TestMissingPredicate) { StatsdConfig config = buildMissingPredicate(); set<int> allTagIds; vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> logTrackerMap; + unordered_map<int64_t, int> atomMatchingTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; + unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; @@ -577,9 +583,9 @@ TEST(MetricsManagerTest, TestMissingPredicate) { std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, - allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, + allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, noReportMetricIds)); } @@ -592,8 +598,9 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { StatsdConfig config = buildCirclePredicates(); set<int> allTagIds; vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> logTrackerMap; + unordered_map<int64_t, int> atomMatchingTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; + unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; @@ -608,9 +615,9 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, - allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, + allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, noReportMetricIds)); } @@ -623,8 +630,9 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { StatsdConfig config = buildAlertWithUnknownMetric(); set<int> allTagIds; vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; - unordered_map<int64_t, int> logTrackerMap; + unordered_map<int64_t, int> atomMatchingTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; + unordered_map<int64_t, int> conditionTrackerMap; vector<sp<MetricProducer>> allMetricProducers; std::vector<sp<AnomalyTracker>> allAnomalyTrackers; std::vector<sp<AlarmTracker>> allAlarmTrackers; @@ -639,9 +647,9 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, - allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap, + allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, noReportMetricIds)); } @@ -649,6 +657,7 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerInvalidMatcher) { sp<UidMap> uidMap = new UidMap(); AtomMatcher matcher; + // Matcher has no contents_case (simple/combination), so it is invalid. matcher.set_id(21); EXPECT_EQ(createAtomMatchingTracker(matcher, 0, uidMap), nullptr); } @@ -699,6 +708,65 @@ TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination) { ASSERT_EQ(atomIds.size(), 0); } +TEST(MetricsManagerTest, TestCreateConditionTrackerInvalid) { + const ConfigKey key(123, 456); + // Predicate has no contents_case (simple/combination), so it is invalid. + Predicate predicate; + predicate.set_id(21); + unordered_map<int64_t, int> atomTrackerMap; + EXPECT_EQ(createConditionTracker(key, predicate, 0, atomTrackerMap), nullptr); +} + +TEST(MetricsManagerTest, TestCreateConditionTrackerSimple) { + int index = 1; + int64_t id = 987; + const ConfigKey key(123, 456); + + int startMatcherIndex = 2, stopMatcherIndex = 0, stopAllMatcherIndex = 1; + int64_t startMatcherId = 246, stopMatcherId = 153, stopAllMatcherId = 975; + + Predicate predicate; + predicate.set_id(id); + SimplePredicate* simplePredicate = predicate.mutable_simple_predicate(); + simplePredicate->set_start(startMatcherId); + simplePredicate->set_stop(stopMatcherId); + simplePredicate->set_stop_all(stopAllMatcherId); + + unordered_map<int64_t, int> atomTrackerMap; + atomTrackerMap[startMatcherId] = startMatcherIndex; + atomTrackerMap[stopMatcherId] = stopMatcherIndex; + atomTrackerMap[stopAllMatcherId] = stopAllMatcherIndex; + + sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap); + EXPECT_EQ(tracker->getConditionId(), id); + EXPECT_EQ(tracker->isSliced(), false); + EXPECT_TRUE(tracker->IsSimpleCondition()); + const set<int>& interestedMatchers = tracker->getAtomMatchingTrackerIndex(); + ASSERT_EQ(interestedMatchers.size(), 3); + ASSERT_EQ(interestedMatchers.count(startMatcherIndex), 1); + ASSERT_EQ(interestedMatchers.count(stopMatcherIndex), 1); + ASSERT_EQ(interestedMatchers.count(stopAllMatcherIndex), 1); +} + +TEST(MetricsManagerTest, TestCreateConditionTrackerCombination) { + int index = 1; + int64_t id = 987; + const ConfigKey key(123, 456); + + Predicate predicate; + predicate.set_id(id); + Predicate_Combination* combinationPredicate = predicate.mutable_combination(); + combinationPredicate->set_operation(LogicalOperation::AND); + combinationPredicate->add_predicate(888); + combinationPredicate->add_predicate(777); + + // Combination conditions must be initialized to set most state. + unordered_map<int64_t, int> atomTrackerMap; + sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap); + EXPECT_EQ(tracker->getConditionId(), id); + EXPECT_FALSE(tracker->IsSimpleCondition()); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/config/Android.bp b/config/Android.bp index 0fb56cb3410a..8dd409b51eee 100644 --- a/config/Android.bp +++ b/config/Android.bp @@ -13,6 +13,6 @@ // limitations under the License. filegroup { - name: "preloaded-classes-blacklist", - srcs: ["preloaded-classes-blacklist"], + name: "preloaded-classes-denylist", + srcs: ["preloaded-classes-denylist"], } diff --git a/config/generate-preloaded-classes.sh b/config/generate-preloaded-classes.sh index 0ad3a0263d95..b17a3660e1f1 100755 --- a/config/generate-preloaded-classes.sh +++ b/config/generate-preloaded-classes.sh @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. if [ "$#" -lt 2 ]; then - echo "Usage $0 <input classes file> <blacklist file> [extra classes files]" + echo "Usage $0 <input classes file> <denylist file> [extra classes files]" exit 1 fi @@ -31,9 +31,9 @@ echo "# Preloaded-classes filter file for phones. #" input=$1 -blacklist=$2 +denylist=$2 shift 2 extra_classes_files=("$@") # Disable locale to enable lexicographical sorting -LC_ALL=C sort "$input" "${extra_classes_files[@]}" | uniq | grep -f "$blacklist" -v -F -x | grep -v "\$NoPreloadHolder" +LC_ALL=C sort "$input" "${extra_classes_files[@]}" | uniq | grep -f "$denylist" -v -F -x | grep -v "\$NoPreloadHolder" diff --git a/config/preloaded-classes-blacklist b/config/preloaded-classes-denylist index 8ab5273cd755..8ab5273cd755 100644 --- a/config/preloaded-classes-blacklist +++ b/config/preloaded-classes-denylist diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 00557114bab0..7087b60d75dd 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1160,9 +1160,17 @@ public class AppOpsManager { // TODO: Add as AppProtoEnums public static final int OP_PHONE_CALL_CAMERA = 101; + /** + * Audio is being recorded for hotword detection. + * + * @hide + */ + // TODO: Add as AppProtoEnums + public static final int OP_RECORD_AUDIO_HOTWORD = 102; + /** @hide */ @UnsupportedAppUsage - public static final int _NUM_OP = 102; + public static final int _NUM_OP = 103; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1497,6 +1505,13 @@ public class AppOpsManager { */ public static final String OPSTR_PHONE_CALL_CAMERA = "android:phone_call_camera"; + /** + * Audio is being recorded for hotword detection. + * + * @hide + */ + public static final String OPSTR_RECORD_AUDIO_HOTWORD = "android:record_audio_hotword"; + /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */ @@ -1688,6 +1703,7 @@ public class AppOpsManager { OP_NO_ISOLATED_STORAGE, // NO_ISOLATED_STORAGE OP_PHONE_CALL_MICROPHONE, // OP_PHONE_CALL_MICROPHONE OP_PHONE_CALL_CAMERA, // OP_PHONE_CALL_CAMERA + OP_RECORD_AUDIO_HOTWORD, // RECORD_AUDIO_HOTWORD }; /** @@ -1796,6 +1812,7 @@ public class AppOpsManager { OPSTR_NO_ISOLATED_STORAGE, OPSTR_PHONE_CALL_MICROPHONE, OPSTR_PHONE_CALL_CAMERA, + OPSTR_RECORD_AUDIO_HOTWORD, }; /** @@ -1905,6 +1922,7 @@ public class AppOpsManager { "NO_ISOLATED_STORAGE", "PHONE_CALL_MICROPHONE", "PHONE_CALL_CAMERA", + "RECORD_AUDIO_HOTWORD", }; /** @@ -2015,6 +2033,7 @@ public class AppOpsManager { null, // no permission for OP_NO_ISOLATED_STORAGE null, // no permission for OP_PHONE_CALL_MICROPHONE null, // no permission for OP_PHONE_CALL_CAMERA + null, // no permission for OP_RECORD_AUDIO_HOTWORD }; /** @@ -2125,6 +2144,7 @@ public class AppOpsManager { null, // NO_ISOLATED_STORAGE null, // PHONE_CALL_MICROPHONE null, // PHONE_CALL_MICROPHONE + null, // RECORD_AUDIO_HOTWORD }; /** @@ -2234,6 +2254,7 @@ public class AppOpsManager { null, // NO_ISOLATED_STORAGE null, // PHONE_CALL_MICROPHONE null, // PHONE_CALL_CAMERA + null, // RECORD_AUDIO_HOTWORD }; /** @@ -2342,6 +2363,7 @@ public class AppOpsManager { AppOpsManager.MODE_ERRORED, // OP_NO_ISOLATED_STORAGE AppOpsManager.MODE_ALLOWED, // PHONE_CALL_MICROPHONE AppOpsManager.MODE_ALLOWED, // PHONE_CALL_CAMERA + AppOpsManager.MODE_ALLOWED, // OP_RECORD_AUDIO_HOTWORD }; /** @@ -2454,6 +2476,7 @@ public class AppOpsManager { true, // NO_ISOLATED_STORAGE false, // PHONE_CALL_MICROPHONE false, // PHONE_CALL_CAMERA + false, // RECORD_AUDIO_HOTWORD }; /** diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 340d5a12f92e..2780036d8102 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -73,6 +73,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.ParcelableException; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; @@ -107,7 +108,11 @@ import dalvik.system.VMRuntime; import libcore.util.EmptyArray; +import java.io.IOException; import java.lang.ref.WeakReference; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -135,6 +140,12 @@ public class ApplicationPackageManager extends PackageManager { // Default flags to use with PackageManager when no flags are given. private static final int sDefaultFlags = GET_SHARED_LIBRARY_FILES; + /** Default set of checksums - includes all available checksums. + * @see PackageManager#getChecksums */ + private static final int DEFAULT_CHECKSUMS = + WHOLE_MERKLE_ROOT_4K_SHA256 | WHOLE_MD5 | WHOLE_SHA1 | WHOLE_SHA256 | WHOLE_SHA512 + | PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512; + // Name of the resource which provides background permission button string public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS = "app_permission_button_allow_always"; @@ -945,6 +956,39 @@ public class ApplicationPackageManager extends PackageManager { } } + private static List<byte[]> encodeCertificates(List<Certificate> certs) throws + CertificateEncodingException { + if (certs == null) { + return null; + } + List<byte[]> result = new ArrayList<>(certs.size()); + for (Certificate cert : certs) { + if (!(cert instanceof X509Certificate)) { + throw new CertificateEncodingException("Only X509 certificates supported."); + } + result.add(cert.getEncoded()); + } + return result; + } + + @Override + public void getChecksums(@NonNull String packageName, boolean includeSplits, + @FileChecksumKind int required, @Nullable List<Certificate> trustedInstallers, + @NonNull IntentSender statusReceiver) + throws CertificateEncodingException, IOException, NameNotFoundException { + Objects.requireNonNull(packageName); + Objects.requireNonNull(statusReceiver); + try { + mPM.getChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required, + encodeCertificates(trustedInstallers), statusReceiver, getUserId()); + } catch (ParcelableException e) { + e.maybeRethrow(PackageManager.NameNotFoundException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Wrap the cached value in a class that does deep compares on string * arrays. The comparison is needed only for the verification mode of diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 7cd3fcad177b..9e4ab33c6aa0 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -54,6 +54,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; @@ -245,7 +246,7 @@ public class ResourcesManager { /** * A cache of DisplayId, DisplayAdjustments to Display. */ - private final ArrayMap<Pair<Integer, DisplayAdjustments>, WeakReference<Display>> + private final ArrayMap<Pair<Integer, DisplayAdjustments>, SoftReference<Display>> mAdjustedDisplays = new ArrayMap<>(); /** @@ -373,25 +374,28 @@ public class ResourcesManager { ? new DisplayAdjustments(displayAdjustments) : new DisplayAdjustments(); final Pair<Integer, DisplayAdjustments> key = Pair.create(displayId, displayAdjustmentsCopy); + SoftReference<Display> sd; synchronized (this) { - WeakReference<Display> wd = mAdjustedDisplays.get(key); - if (wd != null) { - final Display display = wd.get(); - if (display != null) { - return display; - } - } - final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance(); - if (dm == null) { - // may be null early in system startup - return null; - } - final Display display = dm.getCompatibleDisplay(displayId, key.second); + sd = mAdjustedDisplays.get(key); + } + if (sd != null) { + final Display display = sd.get(); if (display != null) { - mAdjustedDisplays.put(key, new WeakReference<>(display)); + return display; + } + } + final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance(); + if (dm == null) { + // may be null early in system startup + return null; + } + final Display display = dm.getCompatibleDisplay(displayId, key.second); + if (display != null) { + synchronized (this) { + mAdjustedDisplays.put(key, new SoftReference<>(display)); } - return display; } + return display; } /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index c61426d5c172..98de85d9735d 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -9751,21 +9751,6 @@ public class DevicePolicyManager { } /** - * @hide - * Return if this user is a system-only user. An admin can manage a device from a system only - * user by calling {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE}. - * @param admin Which device owner this request is associated with. - * @return if this user is a system-only user. - */ - public boolean isSystemOnlyUser(@NonNull ComponentName admin) { - try { - return mService.isSystemOnlyUser(admin); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - - /** * Called by device owner, or profile owner on organization-owned device, to get the MAC * address of the Wi-Fi device. * diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 9c6a274ccf8c..1c7b617e6d9a 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -345,7 +345,6 @@ interface IDevicePolicyManager { void setKeepUninstalledPackages(in ComponentName admin, in String callerPackage, in List<String> packageList); List<String> getKeepUninstalledPackages(in ComponentName admin, in String callerPackage); boolean isManagedProfile(in ComponentName admin); - boolean isSystemOnlyUser(in ComponentName admin); String getWifiMacAddress(in ComponentName admin); void reboot(in ComponentName admin); diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java index 7f436401dbf4..fa135b10ae1f 100644 --- a/core/java/android/app/prediction/AppPredictor.java +++ b/core/java/android/app/prediction/AppPredictor.java @@ -83,6 +83,8 @@ public final class AppPredictor { private final AppPredictionSessionId mSessionId; private final ArrayMap<Callback, CallbackWrapper> mRegisteredCallbacks = new ArrayMap<>(); + private final IBinder mToken = new Binder(); + /** * Creates a new Prediction client. * <p> @@ -98,7 +100,7 @@ public final class AppPredictor { mSessionId = new AppPredictionSessionId( context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId()); try { - mPredictionManager.createPredictionSession(predictionContext, mSessionId); + mPredictionManager.createPredictionSession(predictionContext, mSessionId, mToken); } catch (RemoteException e) { Log.e(TAG, "Failed to create predictor", e); e.rethrowAsRuntimeException(); diff --git a/core/java/android/app/prediction/IPredictionManager.aidl b/core/java/android/app/prediction/IPredictionManager.aidl index 587e3fd52377..863fc6f952dd 100644 --- a/core/java/android/app/prediction/IPredictionManager.aidl +++ b/core/java/android/app/prediction/IPredictionManager.aidl @@ -29,7 +29,7 @@ import android.content.pm.ParceledListSlice; interface IPredictionManager { void createPredictionSession(in AppPredictionContext context, - in AppPredictionSessionId sessionId); + in AppPredictionSessionId sessionId, in IBinder token); void notifyAppTargetEvent(in AppPredictionSessionId sessionId, in AppTargetEvent event); diff --git a/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl b/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl index af77fe05b902..6d0fe72b9de1 100644 --- a/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl +++ b/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl @@ -20,5 +20,5 @@ import android.app.timezonedetector.TimeZoneConfiguration; /** {@hide} */ oneway interface ITimeZoneConfigurationListener { - void onChange(in TimeZoneConfiguration configuration); + void onChange(); }
\ No newline at end of file diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl index 6e93af6a053b..4f7e1f62928a 100644 --- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl +++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl @@ -32,17 +32,15 @@ import android.app.timezonedetector.TimeZoneConfiguration; * this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService} * for more complete documentation. * - * * {@hide} */ interface ITimeZoneDetectorService { TimeZoneCapabilities getCapabilities(); - - TimeZoneConfiguration getConfiguration(); - boolean updateConfiguration(in TimeZoneConfiguration configuration); void addConfigurationListener(ITimeZoneConfigurationListener listener); void removeConfigurationListener(ITimeZoneConfigurationListener listener); + boolean updateConfiguration(in TimeZoneConfiguration configuration); + boolean suggestManualTimeZone(in ManualTimeZoneSuggestion timeZoneSuggestion); void suggestTelephonyTimeZone(in TelephonyTimeZoneSuggestion timeZoneSuggestion); } diff --git a/core/java/android/app/timezonedetector/TEST_MAPPING b/core/java/android/app/timezonedetector/TEST_MAPPING new file mode 100644 index 000000000000..46f2319d69ba --- /dev/null +++ b/core/java/android/app/timezonedetector/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.app.timezonedetector." + } + ] + } + ] +} diff --git a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java b/core/java/android/app/timezonedetector/TimeZoneCapabilities.java index cc0af3f97e49..09fffe9f4f25 100644 --- a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java +++ b/core/java/android/app/timezonedetector/TimeZoneCapabilities.java @@ -16,9 +16,12 @@ package android.app.timezonedetector; +import static android.app.timezonedetector.TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED; +import static android.app.timezonedetector.TimeZoneConfiguration.SETTING_GEO_DETECTION_ENABLED; + import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.UserIdInt; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -38,9 +41,9 @@ import java.util.Objects; * * <p>Actions have associated methods, see the documentation for each action for details. * - * <p>For configuration capabilities, the associated current configuration value can be retrieved - * using {@link TimeZoneDetector#getConfiguration()} and may be changed using - * {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)}. + * <p>For configuration settings capabilities, the associated settings value can be found via + * {@link #getConfiguration()} and may be changed using {@link + * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} (if the user's capabilities allow). * * <p>Note: Capabilities are independent of app permissions required to call the associated APIs. * @@ -60,7 +63,8 @@ public final class TimeZoneCapabilities implements Parcelable { public static final int CAPABILITY_NOT_SUPPORTED = 10; /** - * Indicates that a capability is supported on this device, but not allowed for the user. + * Indicates that a capability is supported on this device, but not allowed for the user, e.g. + * if the capability relates to the ability to modify settings the user is not able to. * This could be because of the user's type (e.g. maybe it applies to the primary user only) or * device policy. Depending on the capability, this could mean the associated UI * should be hidden, or displayed but disabled. @@ -68,9 +72,11 @@ public final class TimeZoneCapabilities implements Parcelable { public static final int CAPABILITY_NOT_ALLOWED = 20; /** - * Indicates that a capability is possessed but not applicable, e.g. if it is configuration, - * the current configuration or device state renders it irrelevant. The associated UI may be - * hidden, disabled, or left visible (but ineffective) depending on requirements. + * Indicates that a capability is possessed but not currently applicable, e.g. if the + * capability relates to the ability to modify settings, the user has the ability to modify + * it, but it is currently rendered irrelevant by other settings or other device state (flags, + * resource config, etc.). The associated UI may be hidden, disabled, or left visible (but + * ineffective) depending on requirements. */ public static final int CAPABILITY_NOT_APPLICABLE = 30; @@ -89,13 +95,13 @@ public final class TimeZoneCapabilities implements Parcelable { }; - private final @UserIdInt int mUserId; + @NonNull private final TimeZoneConfiguration mConfiguration; private final @CapabilityState int mConfigureAutoDetectionEnabled; private final @CapabilityState int mConfigureGeoDetectionEnabled; private final @CapabilityState int mSuggestManualTimeZone; private TimeZoneCapabilities(@NonNull Builder builder) { - this.mUserId = builder.mUserId; + this.mConfiguration = Objects.requireNonNull(builder.mConfiguration); this.mConfigureAutoDetectionEnabled = builder.mConfigureAutoDetectionEnabled; this.mConfigureGeoDetectionEnabled = builder.mConfigureGeoDetectionEnabled; this.mSuggestManualTimeZone = builder.mSuggestManualTimeZone; @@ -103,7 +109,8 @@ public final class TimeZoneCapabilities implements Parcelable { @NonNull private static TimeZoneCapabilities createFromParcel(Parcel in) { - return new TimeZoneCapabilities.Builder(in.readInt()) + return new TimeZoneCapabilities.Builder() + .setConfiguration(in.readParcelable(null)) .setConfigureAutoDetectionEnabled(in.readInt()) .setConfigureGeoDetectionEnabled(in.readInt()) .setSuggestManualTimeZone(in.readInt()) @@ -112,21 +119,24 @@ public final class TimeZoneCapabilities implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mUserId); + dest.writeParcelable(mConfiguration, flags); dest.writeInt(mConfigureAutoDetectionEnabled); dest.writeInt(mConfigureGeoDetectionEnabled); dest.writeInt(mSuggestManualTimeZone); } - /** Returns the user ID the capabilities are for. */ - public @UserIdInt int getUserId() { - return mUserId; + /** + * Returns the user's time zone behavior configuration. + */ + public @NonNull TimeZoneConfiguration getConfiguration() { + return mConfiguration; } /** - * Returns the user's capability state for controlling whether automatic time zone detection is - * enabled via {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link - * TimeZoneConfiguration#isAutoDetectionEnabled()}. + * Returns the capability state associated with the user's ability to modify the automatic time + * zone detection setting. The setting can be updated via {@link + * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and accessed via {@link + * #getConfiguration()}. */ @CapabilityState public int getConfigureAutoDetectionEnabled() { @@ -134,9 +144,10 @@ public final class TimeZoneCapabilities implements Parcelable { } /** - * Returns the user's capability state for controlling whether geolocation can be used to detect - * time zone via {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link - * TimeZoneConfiguration#isGeoDetectionEnabled()}. + * Returns the capability state associated with the user's ability to modify the geolocation + * detection setting. The setting can be updated via {@link + * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and accessed via {@link + * #getConfiguration()}. */ @CapabilityState public int getConfigureGeoDetectionEnabled() { @@ -144,8 +155,8 @@ public final class TimeZoneCapabilities implements Parcelable { } /** - * Returns the user's capability state for manually setting the time zone on a device via - * {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}. + * Returns the capability state associated with the user's ability to manually set the time zone + * on a device via {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}. * * <p>The suggestion will be ignored in all cases unless the value is {@link * #CAPABILITY_POSSESSED}. See also {@link TimeZoneConfiguration#isAutoDetectionEnabled()}. @@ -155,6 +166,38 @@ public final class TimeZoneCapabilities implements Parcelable { return mSuggestManualTimeZone; } + /** + * Constructs a new {@link TimeZoneConfiguration} from an {@code oldConfiguration} and a set of + * {@code requestedChanges}, if the current capabilities allow. The new configuration is + * returned and the capabilities are left unchanged. If the capabilities do not permit one or + * more of the changes then {@code null} is returned. + */ + @Nullable + public TimeZoneConfiguration applyUpdate(TimeZoneConfiguration requestedChanges) { + if (requestedChanges.getUserId() != mConfiguration.getUserId()) { + throw new IllegalArgumentException("User does not match:" + + " this=" + mConfiguration + ", other=" + requestedChanges); + } + + TimeZoneConfiguration.Builder newConfigBuilder = + new TimeZoneConfiguration.Builder(mConfiguration); + if (requestedChanges.hasSetting(SETTING_AUTO_DETECTION_ENABLED)) { + if (getConfigureAutoDetectionEnabled() < CAPABILITY_NOT_APPLICABLE) { + return null; + } + newConfigBuilder.setAutoDetectionEnabled(requestedChanges.isAutoDetectionEnabled()); + } + + if (requestedChanges.hasSetting(SETTING_GEO_DETECTION_ENABLED)) { + if (getConfigureGeoDetectionEnabled() < CAPABILITY_NOT_APPLICABLE) { + return null; + } + newConfigBuilder.setGeoDetectionEnabled(requestedChanges.isGeoDetectionEnabled()); + } + + return newConfigBuilder.build(); + } + @Override public int describeContents() { return 0; @@ -169,7 +212,7 @@ public final class TimeZoneCapabilities implements Parcelable { return false; } TimeZoneCapabilities that = (TimeZoneCapabilities) o; - return mUserId == that.mUserId + return Objects.equals(mConfiguration, that.mConfiguration) && mConfigureAutoDetectionEnabled == that.mConfigureAutoDetectionEnabled && mConfigureGeoDetectionEnabled == that.mConfigureGeoDetectionEnabled && mSuggestManualTimeZone == that.mSuggestManualTimeZone; @@ -177,7 +220,7 @@ public final class TimeZoneCapabilities implements Parcelable { @Override public int hashCode() { - return Objects.hash(mUserId, + return Objects.hash(mConfiguration, mConfigureAutoDetectionEnabled, mConfigureGeoDetectionEnabled, mSuggestManualTimeZone); @@ -186,7 +229,7 @@ public final class TimeZoneCapabilities implements Parcelable { @Override public String toString() { return "TimeZoneDetectorCapabilities{" - + "mUserId=" + mUserId + + "mConfiguration=" + mConfiguration + ", mConfigureAutomaticDetectionEnabled=" + mConfigureAutoDetectionEnabled + ", mConfigureGeoDetectionEnabled=" + mConfigureGeoDetectionEnabled + ", mSuggestManualTimeZone=" + mSuggestManualTimeZone @@ -196,16 +239,18 @@ public final class TimeZoneCapabilities implements Parcelable { /** @hide */ public static class Builder { - private final @UserIdInt int mUserId; + private TimeZoneConfiguration mConfiguration; private @CapabilityState int mConfigureAutoDetectionEnabled; private @CapabilityState int mConfigureGeoDetectionEnabled; private @CapabilityState int mSuggestManualTimeZone; - /** - * Creates a new Builder with no properties set. - */ - public Builder(@UserIdInt int userId) { - mUserId = userId; + /** Sets the user-visible configuration settings. */ + public Builder setConfiguration(@NonNull TimeZoneConfiguration configuration) { + if (!configuration.isComplete()) { + throw new IllegalArgumentException(configuration + " is not complete"); + } + this.mConfiguration = configuration; + return this; } /** Sets the state for the automatic time zone detection enabled config. */ diff --git a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java b/core/java/android/app/timezonedetector/TimeZoneConfiguration.java index 6f84ee22a985..95db0a26cc6e 100644 --- a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java +++ b/core/java/android/app/timezonedetector/TimeZoneConfiguration.java @@ -18,6 +18,7 @@ package android.app.timezonedetector; import android.annotation.NonNull; import android.annotation.StringDef; +import android.annotation.UserIdInt; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -27,21 +28,20 @@ import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** - * Configuration that controls the behavior of the time zone detector associated with a specific - * user. + * User visible settings that control the behavior of the time zone detector / manual time zone + * entry. * - * <p>Configuration consists of a set of known properties. When reading configuration via - * {@link TimeZoneDetector#getConfiguration()} values for all known properties will be provided. In - * some cases, such as when the configuration relies on optional hardware, the values may be - * meaningless / defaulted to safe values. + * <p>When reading the configuration, values for all settings will be provided. In some cases, such + * as when the device behavior relies on optional hardware / OEM configuration, or the value of + * several settings, the device behavior may not be directly affected by the setting value. * - * <p>Configuration properties can be left absent when updating configuration via {@link - * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and those values will not be - * changed. Not all configuration properties can be modified by all users. See {@link - * TimeZoneDetector#getCapabilities()} and {@link TimeZoneCapabilities}. + * <p>Settings can be left absent when updating configuration via {@link + * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and those settings will not be + * changed. Not all configuration settings can be modified by all users: see {@link + * TimeZoneDetector#getCapabilities()} and {@link TimeZoneCapabilities} for details. * - * <p>See {@link #isComplete()} to tell if all known properties are present, and {@link - * #hasProperty(String)} with {@code PROPERTY_} constants for testing individual properties. + * <p>See {@link #hasSetting(String)} with {@code PROPERTY_} constants for testing for the presence + * of individual settings. * * @hide */ @@ -59,80 +59,82 @@ public final class TimeZoneConfiguration implements Parcelable { }; /** All configuration properties */ - @StringDef(PROPERTY_AUTO_DETECTION_ENABLED) + @StringDef({ SETTING_AUTO_DETECTION_ENABLED, SETTING_GEO_DETECTION_ENABLED }) @Retention(RetentionPolicy.SOURCE) - @interface Property {} + @interface Setting {} /** See {@link TimeZoneConfiguration#isAutoDetectionEnabled()} for details. */ - @Property - public static final String PROPERTY_AUTO_DETECTION_ENABLED = "autoDetectionEnabled"; + @Setting + public static final String SETTING_AUTO_DETECTION_ENABLED = "autoDetectionEnabled"; /** See {@link TimeZoneConfiguration#isGeoDetectionEnabled()} for details. */ - @Property - public static final String PROPERTY_GEO_DETECTION_ENABLED = "geoDetectionEnabled"; + @Setting + public static final String SETTING_GEO_DETECTION_ENABLED = "geoDetectionEnabled"; - private final Bundle mBundle; + private final @UserIdInt int mUserId; + @NonNull private final Bundle mBundle; private TimeZoneConfiguration(Builder builder) { - this.mBundle = builder.mBundle; + this.mUserId = builder.mUserId; + this.mBundle = Objects.requireNonNull(builder.mBundle); } private static TimeZoneConfiguration createFromParcel(Parcel in) { - return new TimeZoneConfiguration.Builder() + return new TimeZoneConfiguration.Builder(in.readInt()) .setPropertyBundleInternal(in.readBundle()) .build(); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mUserId); dest.writeBundle(mBundle); } - /** Returns {@code true} if all known properties are set. */ + /** Returns the ID of the user this configuration is associated with. */ + public @UserIdInt int getUserId() { + return mUserId; + } + + /** Returns {@code true} if all known settings are present. */ public boolean isComplete() { - return hasProperty(PROPERTY_AUTO_DETECTION_ENABLED) - && hasProperty(PROPERTY_GEO_DETECTION_ENABLED); + return hasSetting(SETTING_AUTO_DETECTION_ENABLED) + && hasSetting(SETTING_GEO_DETECTION_ENABLED); } - /** Returns true if the specified property is set. */ - public boolean hasProperty(@Property String property) { - return mBundle.containsKey(property); + /** Returns true if the specified setting is set. */ + public boolean hasSetting(@Setting String setting) { + return mBundle.containsKey(setting); } /** - * Returns the value of the {@link #PROPERTY_AUTO_DETECTION_ENABLED} property. This + * Returns the value of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting. This * controls whether a device will attempt to determine the time zone automatically using - * contextual information. + * contextual information if the device supports auto detection. + * + * <p>This setting is global and can be updated by some users. * - * @throws IllegalStateException if the field has not been set + * @throws IllegalStateException if the setting has not been set */ public boolean isAutoDetectionEnabled() { - if (!mBundle.containsKey(PROPERTY_AUTO_DETECTION_ENABLED)) { - throw new IllegalStateException(PROPERTY_AUTO_DETECTION_ENABLED + " is not set"); - } - return mBundle.getBoolean(PROPERTY_AUTO_DETECTION_ENABLED); + enforceSettingPresent(SETTING_AUTO_DETECTION_ENABLED); + return mBundle.getBoolean(SETTING_AUTO_DETECTION_ENABLED); } /** - * Returns the value of the {@link #PROPERTY_GEO_DETECTION_ENABLED} property. This - * controls whether a device can use location to determine time zone. Only used when - * {@link #isAutoDetectionEnabled()} is true. + * Returns the value of the {@link #SETTING_GEO_DETECTION_ENABLED} setting. This + * controls whether a device can use geolocation to determine time zone. Only used when + * {@link #isAutoDetectionEnabled()} is {@code true} and when the user has allowed their + * location to be used. + * + * <p>This setting is user-scoped and can be updated by some users. + * See {@link TimeZoneCapabilities#getConfigureGeoDetectionEnabled()}. * - * @throws IllegalStateException if the field has not been set + * @throws IllegalStateException if the setting has not been set */ public boolean isGeoDetectionEnabled() { - if (!mBundle.containsKey(PROPERTY_GEO_DETECTION_ENABLED)) { - throw new IllegalStateException(PROPERTY_GEO_DETECTION_ENABLED + " is not set"); - } - return mBundle.getBoolean(PROPERTY_GEO_DETECTION_ENABLED); - } - - /** - * Convenience method to merge this with another. The argument configuration properties have - * precedence. - */ - public TimeZoneConfiguration with(TimeZoneConfiguration other) { - return new Builder(this).mergeProperties(other).build(); + enforceSettingPresent(SETTING_GEO_DETECTION_ENABLED); + return mBundle.getBoolean(SETTING_GEO_DETECTION_ENABLED); } @Override @@ -149,43 +151,61 @@ public final class TimeZoneConfiguration implements Parcelable { return false; } TimeZoneConfiguration that = (TimeZoneConfiguration) o; - return mBundle.kindofEquals(that.mBundle); + return mUserId == that.mUserId + && mBundle.kindofEquals(that.mBundle); } @Override public int hashCode() { - return Objects.hash(mBundle); + return Objects.hash(mUserId, mBundle); } @Override public String toString() { return "TimeZoneDetectorConfiguration{" + + "mUserId=" + mUserId + "mBundle=" + mBundle + '}'; } + private void enforceSettingPresent(@Setting String setting) { + if (!mBundle.containsKey(setting)) { + throw new IllegalStateException(setting + " is not set"); + } + } + /** @hide */ public static class Builder { - private Bundle mBundle = new Bundle(); + private final @UserIdInt int mUserId; + private final Bundle mBundle = new Bundle(); /** - * Creates a new Builder with no properties set. + * Creates a new Builder for a userId with no settings held. */ - public Builder() {} + public Builder(@UserIdInt int userId) { + mUserId = userId; + } /** - * Creates a new Builder by copying properties from an existing instance. + * Creates a new Builder by copying the user ID and settings from an existing instance. */ public Builder(TimeZoneConfiguration toCopy) { + this.mUserId = toCopy.mUserId; mergeProperties(toCopy); } /** - * Merges {@code other} properties into this instances, replacing existing values in this - * where the properties appear in both. + * Merges {@code other} settings into this instances, replacing existing values in this + * where the settings appear in both. */ public Builder mergeProperties(TimeZoneConfiguration other) { + if (mUserId != other.mUserId) { + throw new IllegalArgumentException( + "Cannot merge configurations for different user IDs." + + " this.mUserId=" + this.mUserId + + ", other.mUserId=" + other.mUserId); + } this.mBundle.putAll(other.mBundle); return this; } @@ -195,15 +215,19 @@ public final class TimeZoneConfiguration implements Parcelable { return this; } - /** Sets the desired state of the automatic time zone detection property. */ + /** + * Sets the state of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting. + */ public Builder setAutoDetectionEnabled(boolean enabled) { - this.mBundle.putBoolean(PROPERTY_AUTO_DETECTION_ENABLED, enabled); + this.mBundle.putBoolean(SETTING_AUTO_DETECTION_ENABLED, enabled); return this; } - /** Sets the desired state of the geolocation time zone detection enabled property. */ + /** + * Sets the state of the {@link #SETTING_GEO_DETECTION_ENABLED} setting. + */ public Builder setGeoDetectionEnabled(boolean enabled) { - this.mBundle.putBoolean(PROPERTY_GEO_DETECTION_ENABLED, enabled); + this.mBundle.putBoolean(SETTING_GEO_DETECTION_ENABLED, enabled); return this; } diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java index 7885613bfb59..2b1cbf259c55 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetector.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java @@ -37,37 +37,29 @@ public interface TimeZoneDetector { TimeZoneCapabilities getCapabilities(); /** - * Returns the current user's complete time zone configuration. See {@link - * TimeZoneConfiguration}. - */ - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @NonNull - TimeZoneConfiguration getConfiguration(); - - /** * Modifies the time zone detection configuration. * - * <p>Configuration properties vary in scope: some may be device-wide, others may be specific to - * the current user. + * <p>Configuration settings vary in scope: some may be global (affect all users), others may be + * specific to the current user. * - * <p>The ability to modify configuration properties can be subject to restrictions. For + * <p>The ability to modify configuration settings can be subject to restrictions. For * example, they may be determined by device hardware, general policy (i.e. only the primary - * user can set them), or by a managed device policy. See {@link #getCapabilities()} to obtain + * user can set them), or by a managed device policy. Use {@link #getCapabilities()} to obtain * information at runtime about the user's capabilities. * - * <p>Attempts to set configuration with capabilities that are {@link + * <p>Attempts to modify configuration settings with capabilities that are {@link * TimeZoneCapabilities#CAPABILITY_NOT_SUPPORTED} or {@link * TimeZoneCapabilities#CAPABILITY_NOT_ALLOWED} will have no effect and a {@code false} - * will be returned. Setting configuration with capabilities that are {@link + * will be returned. Modifying configuration settings with capabilities that are {@link * TimeZoneCapabilities#CAPABILITY_NOT_APPLICABLE} or {@link * TimeZoneCapabilities#CAPABILITY_POSSESSED} will succeed. See {@link * TimeZoneCapabilities} for further details. * - * <p>If the configuration is not "complete", then only the specified properties will be - * updated (where the user's capabilities allow) and other settings will be left unchanged. See - * {@link TimeZoneConfiguration#isComplete()}. + * <p>If the supplied configuration only has some values set, then only the specified settings + * will be updated (where the user's capabilities allow) and other settings will be left + * unchanged. * - * @return {@code true} if all the configuration properties specified have been set to the + * @return {@code true} if all the configuration settings specified have been set to the * new values, {@code false} if none have */ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @@ -76,14 +68,20 @@ public interface TimeZoneDetector { /** * An interface that can be used to listen for changes to the time zone detector configuration. */ + @FunctionalInterface interface TimeZoneConfigurationListener { - /** Called when the configuration changes. There are no guarantees about the thread used. */ - void onChange(@NonNull TimeZoneConfiguration configuration); + /** + * Called when something about the time zone configuration on the device has changed. + * This could be because the current user has changed, one of the device's relevant settings + * has changed, or something that could affect a user's capabilities has changed. + * There are no guarantees about the thread used. + */ + void onChange(); } /** - * Registers a listener that will be informed when the configuration changes. The complete - * configuration is passed to the listener, not just the properties that have changed. + * Registers a listener that will be informed when something about the time zone configuration + * changes. */ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) void addConfigurationListener(@NonNull TimeZoneConfigurationListener listener); diff --git a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java index 0770aff4e9bb..4c69732abec9 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java @@ -57,19 +57,6 @@ public final class TimeZoneDetectorImpl implements TimeZoneDetector { } @Override - @NonNull - public TimeZoneConfiguration getConfiguration() { - if (DEBUG) { - Log.d(TAG, "getConfiguration called"); - } - try { - return mITimeZoneDetectorService.getConfiguration(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - @Override public boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration) { if (DEBUG) { Log.d(TAG, "updateConfiguration called: " + configuration); @@ -94,8 +81,8 @@ public final class TimeZoneDetectorImpl implements TimeZoneDetector { ITimeZoneConfigurationListener iListener = new ITimeZoneConfigurationListener.Stub() { @Override - public void onChange(@NonNull TimeZoneConfiguration configuration) { - notifyConfigurationListeners(configuration); + public void onChange() { + notifyConfigurationListeners(); } }; mConfigurationReceiver = iListener; @@ -116,14 +103,14 @@ public final class TimeZoneDetectorImpl implements TimeZoneDetector { } } - private void notifyConfigurationListeners(@NonNull TimeZoneConfiguration configuration) { + private void notifyConfigurationListeners() { final ArraySet<TimeZoneConfigurationListener> configurationListeners; synchronized (this) { configurationListeners = new ArraySet<>(mConfigurationListeners); } int size = configurationListeners.size(); for (int i = 0; i < size; i++) { - configurationListeners.valueAt(i).onChange(configuration); + configurationListeners.valueAt(i).onChange(); } } diff --git a/core/java/android/content/pm/FileChecksum.aidl b/core/java/android/content/pm/FileChecksum.aidl new file mode 100644 index 000000000000..109f211033c1 --- /dev/null +++ b/core/java/android/content/pm/FileChecksum.aidl @@ -0,0 +1,20 @@ +/* + * 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.content.pm; + +parcelable FileChecksum; + diff --git a/core/java/android/content/pm/FileChecksum.java b/core/java/android/content/pm/FileChecksum.java new file mode 100644 index 000000000000..55430c2b877b --- /dev/null +++ b/core/java/android/content/pm/FileChecksum.java @@ -0,0 +1,238 @@ +/* + * 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.content.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.IntentSender; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.List; + +/** + * A typed checksum. + * + * @see PackageManager#getChecksums(String, boolean, int, List, IntentSender) + */ +@DataClass(genHiddenConstructor = true) +public final class FileChecksum implements Parcelable { + /** + * Checksum for which split. Null indicates base.apk. + */ + private final @Nullable String mSplitName; + /** + * Checksum kind. + */ + private final @PackageManager.FileChecksumKind int mKind; + /** + * Checksum value. + */ + private final @NonNull byte[] mValue; + /** + * For Installer-provided checksums, certificate of the Installer/AppStore. + */ + private final @Nullable byte[] mSourceCertificate; + + /** + * Constructor, internal use only + * + * @hide + */ + public FileChecksum(@Nullable String splitName, @PackageManager.FileChecksumKind int kind, + @NonNull byte[] value) { + this(splitName, kind, value, (byte[]) null); + } + + /** + * Constructor, internal use only + * + * @hide + */ + public FileChecksum(@Nullable String splitName, @PackageManager.FileChecksumKind int kind, + @NonNull byte[] value, @Nullable Certificate sourceCertificate) + throws CertificateEncodingException { + this(splitName, kind, value, + (sourceCertificate != null) ? sourceCertificate.getEncoded() : null); + } + + /** + * Certificate of the source of this checksum. + * @throws CertificateException in case when certificate can't be re-created from serialized + * data. + */ + public @Nullable Certificate getSourceCertificate() throws CertificateException { + if (mSourceCertificate == null) { + return null; + } + final CertificateFactory cf = CertificateFactory.getInstance("X.509"); + final InputStream is = new ByteArrayInputStream(mSourceCertificate); + final X509Certificate cert = (X509Certificate) cf.generateCertificate(is); + return cert; + } + + + + // Code below generated by codegen v1.0.15. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/FileChecksum.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new FileChecksum. + * + * @param splitName + * Checksum for which split. Null indicates base.apk. + * @param kind + * Checksum kind. + * @param value + * Checksum value. + * @param sourceCertificate + * For Installer-provided checksums, certificate of the Installer/AppStore. + * @hide + */ + @DataClass.Generated.Member + public FileChecksum( + @Nullable String splitName, + @PackageManager.FileChecksumKind int kind, + @NonNull byte[] value, + @Nullable byte[] sourceCertificate) { + this.mSplitName = splitName; + this.mKind = kind; + com.android.internal.util.AnnotationValidations.validate( + PackageManager.FileChecksumKind.class, null, mKind); + this.mValue = value; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mValue); + this.mSourceCertificate = sourceCertificate; + + // onConstructed(); // You can define this method to get a callback + } + + /** + * Checksum for which split. Null indicates base.apk. + */ + @DataClass.Generated.Member + public @Nullable String getSplitName() { + return mSplitName; + } + + /** + * Checksum kind. + */ + @DataClass.Generated.Member + public @PackageManager.FileChecksumKind int getKind() { + return mKind; + } + + /** + * Checksum value. + */ + @DataClass.Generated.Member + public @NonNull byte[] getValue() { + return mValue; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mSplitName != null) flg |= 0x1; + if (mSourceCertificate != null) flg |= 0x8; + dest.writeByte(flg); + if (mSplitName != null) dest.writeString(mSplitName); + dest.writeInt(mKind); + dest.writeByteArray(mValue); + if (mSourceCertificate != null) dest.writeByteArray(mSourceCertificate); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ FileChecksum(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + String splitName = (flg & 0x1) == 0 ? null : in.readString(); + int kind = in.readInt(); + byte[] value = in.createByteArray(); + byte[] sourceCertificate = (flg & 0x8) == 0 ? null : in.createByteArray(); + + this.mSplitName = splitName; + this.mKind = kind; + com.android.internal.util.AnnotationValidations.validate( + PackageManager.FileChecksumKind.class, null, mKind); + this.mValue = value; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mValue); + this.mSourceCertificate = sourceCertificate; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<FileChecksum> CREATOR + = new Parcelable.Creator<FileChecksum>() { + @Override + public FileChecksum[] newArray(int size) { + return new FileChecksum[size]; + } + + @Override + public FileChecksum createFromParcel(@NonNull Parcel in) { + return new FileChecksum(in); + } + }; + + @DataClass.Generated( + time = 1598322801861L, + codegenVersion = "1.0.15", + sourceFile = "frameworks/base/core/java/android/content/pm/FileChecksum.java", + inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.content.pm.PackageManager.FileChecksumKind int mKind\nprivate final @android.annotation.NonNull byte[] mValue\nprivate final @android.annotation.Nullable byte[] mSourceCertificate\npublic @android.annotation.Nullable java.security.cert.Certificate getSourceCertificate()\nclass FileChecksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 6a8dd81051eb..1f8cee25be51 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -743,6 +743,8 @@ interface IPackageManager { void notifyPackagesReplacedReceived(in String[] packages); + void getChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId); + //------------------------------------------------------------------------ // // The following binder interfaces have been moved to IPermissionManager diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 7b2955db3318..da8d15af92b8 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -79,8 +79,11 @@ import com.android.internal.util.ArrayUtils; import dalvik.system.VMRuntime; import java.io.File; +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -3305,6 +3308,13 @@ public abstract class PackageManager { public static final String EXTRA_FAILURE_EXISTING_PERMISSION = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION"; + /** + * Extra field name for the ID of a package pending verification. Passed to + * a package verifier and is used to call back to + * @see #getChecksums + */ + public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS"; + /** * Permission flag: The permission is set in its current state * by the user and apps can still request it at runtime. @@ -7842,6 +7852,114 @@ public abstract class PackageManager { } /** + * Root SHA256 hash of a 4K Merkle tree computed over all file bytes. + * <a href="https://source.android.com/security/apksigning/v4">See APK Signature Scheme V4</a>. + * <a href="https://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git/tree/Documentation/filesystems/fsverity.rst">See fs-verity</a>. + * + * @see #getChecksums + */ + public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001; + + /** + * MD5 hash computed over all file bytes. + * + * @see #getChecksums + */ + public static final int WHOLE_MD5 = 0x00000002; + + /** + * SHA1 hash computed over all file bytes. + * + * @see #getChecksums + */ + public static final int WHOLE_SHA1 = 0x00000004; + + /** + * SHA256 hash computed over all file bytes. + * + * @see #getChecksums + */ + public static final int WHOLE_SHA256 = 0x00000008; + + /** + * SHA512 hash computed over all file bytes. + * + * @see #getChecksums + */ + public static final int WHOLE_SHA512 = 0x00000010; + + /** + * Root SHA256 hash of a 1M Merkle tree computed over protected content. + * Excludes signing block. + * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>. + * + * @see #getChecksums + */ + public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020; + + /** + * Root SHA512 hash of a 1M Merkle tree computed over protected content. + * Excludes signing block. + * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>. + * + * @see #getChecksums + */ + public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040; + + /** @hide */ + @IntDef(flag = true, prefix = {"WHOLE_", "PARTIAL_"}, value = { + WHOLE_MERKLE_ROOT_4K_SHA256, + WHOLE_MD5, + WHOLE_SHA1, + WHOLE_SHA256, + WHOLE_SHA512, + PARTIAL_MERKLE_ROOT_1M_SHA256, + PARTIAL_MERKLE_ROOT_1M_SHA512, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FileChecksumKind {} + + /** + * Trust any Installer to provide checksums for the package. + * @see #getChecksums + */ + public static final @Nullable List<Certificate> TRUST_ALL = null; + + /** + * Don't trust any Installer to provide checksums for the package. + * This effectively disables optimized Installer-enforced checksums. + * @see #getChecksums + */ + public static final @NonNull List<Certificate> TRUST_NONE = Collections.emptyList(); + + /** + * Returns the checksums for APKs within a package. + * + * By default returns all readily available checksums: + * - enforced by platform, + * - enforced by installer. + * If caller needs a specific checksum kind, they can specify it as required. + * + * @param packageName whose checksums to return. + * @param includeSplits whether to include checksums for non-base splits. + * @param required explicitly request the checksum kinds. Will incur significant + * CPU/memory/disk usage. + * @param trustedInstallers for checksums enforced by Installer, which ones to be trusted. + * {@link #TRUST_ALL} will return checksums from any Installer, + * {@link #TRUST_NONE} disables optimized Installer-enforced checksums. + * @param statusReceiver called once when the results are available as + * {@link #EXTRA_CHECKSUMS} of type FileChecksum[]. + * @throws CertificateEncodingException if an encoding error occurs for trustedInstallers. + * @throws NameNotFoundException if a package with the given name cannot be found on the system. + */ + public void getChecksums(@NonNull String packageName, boolean includeSplits, + @FileChecksumKind int required, @Nullable List<Certificate> trustedInstallers, + @NonNull IntentSender statusReceiver) + throws CertificateEncodingException, IOException, NameNotFoundException { + throw new UnsupportedOperationException("getChecksums not implemented in subclass"); + } + + /** * @return the default text classifier package name, or null if there's none. * * @hide diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 2465b0e41876..81a147c68e2e 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -43,7 +43,6 @@ import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.UserInfo; import android.content.pm.UserInfo.UserInfoFlag; -import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -3174,28 +3173,55 @@ public class UserManager { } /** - * Returns information for all users on this device, including ones marked for deletion. - * To retrieve only users that are alive, use {@link #getUsers(boolean)}. + * Returns information for all fully-created users on this device, including ones marked for + * deletion. + * + * <p>To retrieve only users that are not marked for deletion, use {@link #getAliveUsers()}. + * + * <p>To retrieve *all* users (including partial and pre-created users), use + * {@link #getUsers(boolean, boolean, boolean)) getUsers(false, false, false)}. + * + * <p>To retrieve a more specific list of users, use + * {@link #getUsers(boolean, boolean, boolean)}. + * + * @return the list of users that were created. * - * @return the list of users that exist on the device. * @hide */ @UnsupportedAppUsage @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public List<UserInfo> getUsers() { - return getUsers(/* excludeDying= */ false); + return getUsers(/*excludePartial= */ true, /* excludeDying= */ false, + /* excludePreCreated= */ true); } /** - * Returns information for all users on this device. Requires - * {@link android.Manifest.permission#MANAGE_USERS} permission. + * Returns information for all "usable" users on this device (i.e, it excludes users that are + * marked for deletion, pre-created users, etc...). + * + * <p>To retrieve all fully-created users, use {@link #getUsers()}. + * + * <p>To retrieve a more specific list of users, use + * {@link #getUsers(boolean, boolean, boolean)}. * - * @param excludeDying specify if the list should exclude users being - * removed. * @return the list of users that were created. * @hide */ + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + public @NonNull List<UserInfo> getAliveUsers() { + return getUsers(/*excludePartial= */ true, /* excludeDying= */ true, + /* excludePreCreated= */ true); + } + + /** + * @deprecated use {@link #getAliveUsers()} for {@code getUsers(true)}, or + * {@link #getUsers()} for @code getUsers(false)}. + * + * @hide + */ + @Deprecated @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public @NonNull List<UserInfo> getUsers(boolean excludeDying) { return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); diff --git a/core/java/android/preference/OWNERS b/core/java/android/preference/OWNERS index d20511fcfdf2..827134e8fc9d 100644 --- a/core/java/android/preference/OWNERS +++ b/core/java/android/preference/OWNERS @@ -1,2 +1,3 @@ +lpf@google.com pavlis@google.com clarabayarri@google.com diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 327bca268a7b..d55fc511fc77 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -218,8 +218,15 @@ public abstract class DocumentsProvider extends ContentProvider { } /** {@hide} */ - private void enforceTree(Uri documentUri) { - if (isTreeUri(documentUri)) { + private void enforceTreeForExtraUris(Bundle extras) { + enforceTree(extras.getParcelable(DocumentsContract.EXTRA_URI)); + enforceTree(extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI)); + enforceTree(extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI)); + } + + /** {@hide} */ + private void enforceTree(@Nullable Uri documentUri) { + if (documentUri != null && isTreeUri(documentUri)) { final String parent = getTreeDocumentId(documentUri); final String child = getDocumentId(documentUri); if (Objects.equals(parent, child)) { @@ -1076,6 +1083,9 @@ public abstract class DocumentsProvider extends ContentProvider { final Context context = getContext(); final Bundle out = new Bundle(); + // If the URI is a tree URI performs some validation. + enforceTreeForExtraUris(extras); + if (METHOD_EJECT_ROOT.equals(method)) { // Given that certain system apps can hold MOUNT_UNMOUNT permission, but only apps // signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for @@ -1099,9 +1109,6 @@ public abstract class DocumentsProvider extends ContentProvider { "Requested authority " + authority + " doesn't match provider " + mAuthority); } - // If the URI is a tree URI performs some validation. - enforceTree(documentUri); - if (METHOD_IS_CHILD_DOCUMENT.equals(method)) { enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingAttributionTag(), null); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c302def19298..03cf0cf2ca78 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6404,6 +6404,17 @@ public final class Settings { public static final int LOCATION_MODE_ON = LOCATION_MODE_HIGH_ACCURACY; /** + * The current location time zone detection enabled state for the user. + * + * See {@link + * android.app.timezonedetector.TimeZoneDetector#getCapabilities} for access. See {@link + * android.app.timezonedetector.TimeZoneDetector#updateConfiguration} to update. + * @hide + */ + public static final String LOCATION_TIME_ZONE_DETECTION_ENABLED = + "location_time_zone_detection_enabled"; + + /** * The accuracy in meters used for coarsening location for clients with only the coarse * location permission. * diff --git a/core/java/android/text/OWNERS b/core/java/android/text/OWNERS index e56137119c28..0b51b2d90b79 100644 --- a/core/java/android/text/OWNERS +++ b/core/java/android/text/OWNERS @@ -1,5 +1,4 @@ set noparent siyamed@google.com -nona@google.com -clarabayarri@google.com +nona@google.com
\ No newline at end of file diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index 6e34666aea84..f74990a82327 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -149,7 +149,7 @@ public class ApkSignatureSchemeV2Verifier { * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2. * @throws IOException if an I/O error occurs while reading the APK file. */ - private static SignatureInfo findSignature(RandomAccessFile apk) + public static SignatureInfo findSignature(RandomAccessFile apk) throws IOException, SignatureNotFoundException { return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V2_BLOCK_ID); } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java index 93572857796c..5f963b019335 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -142,7 +142,7 @@ public class ApkSignatureSchemeV3Verifier { * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3. * @throws IOException if an I/O error occurs while reading the APK file. */ - private static SignatureInfo findSignature(RandomAccessFile apk) + public static SignatureInfo findSignature(RandomAccessFile apk) throws IOException, SignatureNotFoundException { return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V3_BLOCK_ID); } diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index e0258f7657b2..02edb7ed50a5 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -92,6 +92,20 @@ public class ApkSignatureVerifier { private static PackageParser.SigningDetails verifySignatures(String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) throws PackageParserException { + return verifySignaturesInternal(apkPath, minSignatureSchemeVersion, + verifyFull).signingDetails; + } + + /** + * Verifies the provided APK using all allowed signing schemas. + * @return the certificates associated with each signer and content digests. + * @param verifyFull whether to verify all contents of this APK or just collect certificates. + * @throws PackageParserException if there was a problem collecting certificates + * @hide + */ + public static SigningDetailsWithDigests verifySignaturesInternal(String apkPath, + @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) + throws PackageParserException { if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V4) { // V3 and before are older than the requested minimum signing version @@ -121,7 +135,7 @@ public class ApkSignatureVerifier { return verifyV3AndBelowSignatures(apkPath, minSignatureSchemeVersion, verifyFull); } - private static PackageParser.SigningDetails verifyV3AndBelowSignatures(String apkPath, + private static SigningDetailsWithDigests verifyV3AndBelowSignatures(String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) throws PackageParserException { // try v3 @@ -174,7 +188,7 @@ public class ApkSignatureVerifier { * @throws SignatureNotFoundException if there are no V4 signatures in the APK * @throws PackageParserException if there was a problem collecting certificates */ - private static PackageParser.SigningDetails verifyV4Signature(String apkPath, + private static SigningDetailsWithDigests verifyV4Signature(String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) throws SignatureNotFoundException, PackageParserException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4"); @@ -234,8 +248,8 @@ public class ApkSignatureVerifier { } } - return new PackageParser.SigningDetails(signerSigs, - SignatureSchemeVersion.SIGNING_BLOCK_V4); + return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs, + SignatureSchemeVersion.SIGNING_BLOCK_V4), vSigner.contentDigests); } catch (SignatureNotFoundException e) { throw e; } catch (Exception e) { @@ -256,8 +270,8 @@ public class ApkSignatureVerifier { * @throws SignatureNotFoundException if there are no V3 signatures in the APK * @throws PackageParserException if there was a problem collecting certificates */ - private static PackageParser.SigningDetails verifyV3Signature(String apkPath, - boolean verifyFull) throws SignatureNotFoundException, PackageParserException { + private static SigningDetailsWithDigests verifyV3Signature(String apkPath, boolean verifyFull) + throws SignatureNotFoundException, PackageParserException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV3" : "certsOnlyV3"); try { ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner = @@ -275,8 +289,9 @@ public class ApkSignatureVerifier { pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i)); } } - return new PackageParser.SigningDetails(signerSigs, - SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs); + return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs, + SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs), + vSigner.contentDigests); } catch (SignatureNotFoundException e) { throw e; } catch (Exception e) { @@ -297,15 +312,16 @@ public class ApkSignatureVerifier { * @throws SignatureNotFoundException if there are no V2 signatures in the APK * @throws PackageParserException if there was a problem collecting certificates */ - private static PackageParser.SigningDetails verifyV2Signature(String apkPath, - boolean verifyFull) throws SignatureNotFoundException, PackageParserException { + private static SigningDetailsWithDigests verifyV2Signature(String apkPath, boolean verifyFull) + throws SignatureNotFoundException, PackageParserException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV2" : "certsOnlyV2"); try { - Certificate[][] signerCerts = verifyFull ? ApkSignatureSchemeV2Verifier.verify(apkPath) - : ApkSignatureSchemeV2Verifier.unsafeGetCertsWithoutVerification(apkPath); + ApkSignatureSchemeV2Verifier.VerifiedSigner vSigner = + ApkSignatureSchemeV2Verifier.verify(apkPath, verifyFull); + Certificate[][] signerCerts = vSigner.certs; Signature[] signerSigs = convertToSignatures(signerCerts); - return new PackageParser.SigningDetails(signerSigs, - SignatureSchemeVersion.SIGNING_BLOCK_V2); + return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs, + SignatureSchemeVersion.SIGNING_BLOCK_V2), vSigner.contentDigests); } catch (SignatureNotFoundException e) { throw e; } catch (Exception e) { @@ -324,8 +340,7 @@ public class ApkSignatureVerifier { * @param verifyFull whether to verify all contents of this APK or just collect certificates. * @throws PackageParserException if there was a problem collecting certificates */ - private static PackageParser.SigningDetails verifyV1Signature( - String apkPath, boolean verifyFull) + private static SigningDetailsWithDigests verifyV1Signature(String apkPath, boolean verifyFull) throws PackageParserException { StrictJarFile jarFile = null; @@ -391,7 +406,8 @@ public class ApkSignatureVerifier { } } } - return new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR); + return new SigningDetailsWithDigests( + new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null); } catch (GeneralSecurityException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, "Failed to collect certificates from " + apkPath, e); @@ -542,4 +558,27 @@ public class ApkSignatureVerifier { return null; } } + + /** + * Extended signing details. + * @hide for internal use only. + */ + public static class SigningDetailsWithDigests { + public final PackageParser.SigningDetails signingDetails; + + /** + * APK Signature Schemes v2/v3/v4 might contain multiple content digests. + * SignatureVerifier usually chooses one of them to verify. + * For certain signature schemes, e.g. v4, this digest is verified continuously. + * For others, e.g. v2, the caller has to specify if they want to verify. + * Please refer to documentation for more details. + */ + public final Map<Integer, byte[]> contentDigests; + + SigningDetailsWithDigests(PackageParser.SigningDetails signingDetails, + Map<Integer, byte[]> contentDigests) { + this.signingDetails = signingDetails; + this.contentDigests = contentDigests; + } + } } diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java index 990092caa833..021f232979ef 100644 --- a/core/java/android/util/apk/ApkSigningBlockUtils.java +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -39,7 +39,7 @@ import java.util.Map; * * @hide for internal use only. */ -final class ApkSigningBlockUtils { +public final class ApkSigningBlockUtils { private ApkSigningBlockUtils() { } @@ -146,6 +146,37 @@ final class ApkSigningBlockUtils { Map<Integer, byte[]> expectedDigests, FileDescriptor apkFileDescriptor, SignatureInfo signatureInfo) throws SecurityException { + int[] digestAlgorithms = new int[expectedDigests.size()]; + int digestAlgorithmCount = 0; + for (int digestAlgorithm : expectedDigests.keySet()) { + digestAlgorithms[digestAlgorithmCount] = digestAlgorithm; + digestAlgorithmCount++; + } + byte[][] actualDigests; + try { + actualDigests = computeContentDigestsPer1MbChunk(digestAlgorithms, apkFileDescriptor, + signatureInfo); + } catch (DigestException e) { + throw new SecurityException("Failed to compute digest(s) of contents", e); + } + for (int i = 0; i < digestAlgorithms.length; i++) { + int digestAlgorithm = digestAlgorithms[i]; + byte[] expectedDigest = expectedDigests.get(digestAlgorithm); + byte[] actualDigest = actualDigests[i]; + if (!MessageDigest.isEqual(expectedDigest, actualDigest)) { + throw new SecurityException( + getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm) + + " digest of contents did not verify"); + } + } + } + + /** + * Calculate digests using digestAlgorithms for apkFileDescriptor. + * This will skip signature block described by signatureInfo. + */ + public static byte[][] computeContentDigestsPer1MbChunk(int[] digestAlgorithms, + FileDescriptor apkFileDescriptor, SignatureInfo signatureInfo) throws DigestException { // We need to verify the integrity of the following three sections of the file: // 1. Everything up to the start of the APK Signing Block. // 2. ZIP Central Directory. @@ -156,6 +187,7 @@ final class ApkSigningBlockUtils { // avoid wasting physical memory. In most APK verification scenarios, the contents of the // APK are already there in the OS's page cache and thus mmap does not use additional // physical memory. + DataSource beforeApkSigningBlock = new MemoryMappedFileDataSource(apkFileDescriptor, 0, signatureInfo.apkSigningBlockOffset); @@ -171,31 +203,8 @@ final class ApkSigningBlockUtils { ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, signatureInfo.apkSigningBlockOffset); DataSource eocd = new ByteBufferDataSource(eocdBuf); - int[] digestAlgorithms = new int[expectedDigests.size()]; - int digestAlgorithmCount = 0; - for (int digestAlgorithm : expectedDigests.keySet()) { - digestAlgorithms[digestAlgorithmCount] = digestAlgorithm; - digestAlgorithmCount++; - } - byte[][] actualDigests; - try { - actualDigests = - computeContentDigestsPer1MbChunk( - digestAlgorithms, - new DataSource[] {beforeApkSigningBlock, centralDir, eocd}); - } catch (DigestException e) { - throw new SecurityException("Failed to compute digest(s) of contents", e); - } - for (int i = 0; i < digestAlgorithms.length; i++) { - int digestAlgorithm = digestAlgorithms[i]; - byte[] expectedDigest = expectedDigests.get(digestAlgorithm); - byte[] actualDigest = actualDigests[i]; - if (!MessageDigest.isEqual(expectedDigest, actualDigest)) { - throw new SecurityException( - getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm) - + " digest of contents did not verify"); - } - } + return computeContentDigestsPer1MbChunk(digestAlgorithms, + new DataSource[]{beforeApkSigningBlock, centralDir, eocd}); } private static byte[][] computeContentDigestsPer1MbChunk( @@ -417,14 +426,10 @@ final class ApkSigningBlockUtils { static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0423; static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0425; - static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1; - static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2; - static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3; - static final int CONTENT_DIGEST_SHA256 = 4; - - private static final int[] V4_CONTENT_DIGEST_ALGORITHMS = - {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256, - CONTENT_DIGEST_CHUNKED_SHA256}; + public static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1; + public static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2; + public static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3; + public static final int CONTENT_DIGEST_SHA256 = 4; static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) { int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1); diff --git a/core/java/android/util/apk/SignatureInfo.java b/core/java/android/util/apk/SignatureInfo.java index 8e1233af34a1..7638293618ba 100644 --- a/core/java/android/util/apk/SignatureInfo.java +++ b/core/java/android/util/apk/SignatureInfo.java @@ -16,15 +16,18 @@ package android.util.apk; +import android.annotation.NonNull; + import java.nio.ByteBuffer; /** * APK Signature Scheme v2 block and additional information relevant to verifying the signatures * contained in the block against the file. + * @hide */ -class SignatureInfo { +public class SignatureInfo { /** Contents of APK Signature Scheme v2 block. */ - public final ByteBuffer signatureBlock; + public final @NonNull ByteBuffer signatureBlock; /** Position of the APK Signing Block in the file. */ public final long apkSigningBlockOffset; @@ -36,10 +39,10 @@ class SignatureInfo { public final long eocdOffset; /** Contents of ZIP End of Central Directory (EoCD) of the file. */ - public final ByteBuffer eocd; + public final @NonNull ByteBuffer eocd; - SignatureInfo(ByteBuffer signatureBlock, long apkSigningBlockOffset, long centralDirOffset, - long eocdOffset, ByteBuffer eocd) { + SignatureInfo(@NonNull ByteBuffer signatureBlock, long apkSigningBlockOffset, + long centralDirOffset, long eocdOffset, @NonNull ByteBuffer eocd) { this.signatureBlock = signatureBlock; this.apkSigningBlockOffset = apkSigningBlockOffset; this.centralDirOffset = centralDirOffset; diff --git a/core/java/android/util/apk/VerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java index e81e3f7b38d6..4596c6e8f83d 100644 --- a/core/java/android/util/apk/VerityBuilder.java +++ b/core/java/android/util/apk/VerityBuilder.java @@ -116,6 +116,34 @@ public abstract class VerityBuilder { } /** + * Generates the fs-verity hash tree. It is the actual verity tree format on disk, as is + * re-generated on device. + * + * The tree is built bottom up. The bottom level has 256-bit digest for each 4 KB block in the + * input file. If the total size is larger than 4 KB, take this level as input and repeat the + * same procedure, until the level is within 4 KB. If salt is given, it will apply to each + * digestion before the actual data. + * + * The returned root hash is calculated from the last level of 4 KB chunk, similarly with salt. + * + * @return the root hash of the generated hash tree. + */ + public static byte[] generateFsVerityRootHash(@NonNull String apkPath, byte[] salt, + @NonNull ByteBufferFactory bufferFactory) + throws IOException, NoSuchAlgorithmException, DigestException { + try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { + int[] levelOffset = calculateVerityLevelOffset(apk.length()); + int merkleTreeSize = levelOffset[levelOffset.length - 1]; + + ByteBuffer output = bufferFactory.create( + merkleTreeSize + + CHUNK_SIZE_BYTES); // maximum size of apk-verity metadata + output.order(ByteOrder.LITTLE_ENDIAN); + ByteBuffer tree = slice(output, 0, merkleTreeSize); + return generateFsVerityTreeInternal(apk, salt, levelOffset, tree); + } + } + /** * Calculates the apk-verity root hash for integrity measurement. This needs to be consistent * to what kernel returns. */ @@ -259,9 +287,10 @@ public abstract class VerityBuilder { // thus the syscall overhead is not too big. private static final int MMAP_REGION_SIZE_BYTES = 1024 * 1024; - private static void generateFsVerityDigestAtLeafLevel(RandomAccessFile file, ByteBuffer output) + private static void generateFsVerityDigestAtLeafLevel(RandomAccessFile file, + @Nullable byte[] salt, ByteBuffer output) throws IOException, NoSuchAlgorithmException, DigestException { - BufferedDigester digester = new BufferedDigester(null /* salt */, output); + BufferedDigester digester = new BufferedDigester(salt, output); // 1. Digest the whole file by chunks. consumeByChunk(digester, @@ -325,6 +354,35 @@ public abstract class VerityBuilder { } @NonNull + private static byte[] generateFsVerityTreeInternal(@NonNull RandomAccessFile apk, + @Nullable byte[] salt, @NonNull int[] levelOffset, @NonNull ByteBuffer output) + throws IOException, NoSuchAlgorithmException, DigestException { + // 1. Digest the apk to generate the leaf level hashes. + generateFsVerityDigestAtLeafLevel(apk, salt, + slice(output, levelOffset[levelOffset.length - 2], + levelOffset[levelOffset.length - 1])); + + // 2. Digest the lower level hashes bottom up. + for (int level = levelOffset.length - 3; level >= 0; level--) { + ByteBuffer inputBuffer = slice(output, levelOffset[level + 1], levelOffset[level + 2]); + ByteBuffer outputBuffer = slice(output, levelOffset[level], levelOffset[level + 1]); + + DataSource source = new ByteBufferDataSource(inputBuffer); + BufferedDigester digester = new BufferedDigester(salt, outputBuffer); + consumeByChunk(digester, source, CHUNK_SIZE_BYTES); + digester.assertEmptyBuffer(); + digester.fillUpLastOutputChunk(); + } + + // 3. Digest the first block (i.e. first level) to generate the root hash. + byte[] rootHash = new byte[DIGEST_SIZE_BYTES]; + BufferedDigester digester = new BufferedDigester(salt, ByteBuffer.wrap(rootHash)); + digester.consume(slice(output, 0, CHUNK_SIZE_BYTES)); + digester.assertEmptyBuffer(); + return rootHash; + } + + @NonNull private static byte[] generateVerityTreeInternal(@NonNull RandomAccessFile apk, @Nullable SignatureInfo signatureInfo, @Nullable byte[] salt, @NonNull int[] levelOffset, @NonNull ByteBuffer output) diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index 6136a80978b7..0c3d61f31dfb 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -456,8 +456,8 @@ public class NotificationHeaderView extends ViewGroup { case MotionEvent.ACTION_UP: if (mTrackGesture) { if (mFeedbackIcon.isVisibleToUser() - && (mFeedbackRect.contains((int) x, (int) y)) - || mFeedbackRect.contains((int) mDownX, (int) mDownY)) { + && (mFeedbackRect.contains((int) x, (int) y) + || mFeedbackRect.contains((int) mDownX, (int) mDownY))) { mFeedbackIcon.performClick(); return true; } diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl index 92fa80e40caf..12b16ff6645c 100644 --- a/core/java/android/window/ITaskOrganizerController.aidl +++ b/core/java/android/window/ITaskOrganizerController.aidl @@ -60,5 +60,6 @@ interface ITaskOrganizerController { * Requests that the given task organizer is notified when back is pressed on the root activity * of one of its controlled tasks. */ - void setInterceptBackPressedOnTaskRoot(ITaskOrganizer organizer, boolean interceptBackPressed); + void setInterceptBackPressedOnTaskRoot(in WindowContainerToken task, + boolean interceptBackPressed); } diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index 7ec4f99ce959..38fb023a0822 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -149,9 +149,10 @@ public class TaskOrganizer extends WindowOrganizer { * of one of its controlled tasks. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) - public void setInterceptBackPressedOnTaskRoot(boolean interceptBackPressed) { + public void setInterceptBackPressedOnTaskRoot(@NonNull WindowContainerToken task, + boolean interceptBackPressed) { try { - getController().setInterceptBackPressedOnTaskRoot(mInterface, interceptBackPressed); + getController().setInterceptBackPressedOnTaskRoot(task, interceptBackPressed); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/window/TaskOrganizerTaskEmbedder.java b/core/java/android/window/TaskOrganizerTaskEmbedder.java index 46c72f88e14b..eb9dfed7f644 100644 --- a/core/java/android/window/TaskOrganizerTaskEmbedder.java +++ b/core/java/android/window/TaskOrganizerTaskEmbedder.java @@ -74,7 +74,7 @@ public class TaskOrganizerTaskEmbedder extends TaskEmbedder { // windowing mode tasks. Plan is to migrate this to a wm-shell front-end when that // infrastructure is ready. // mTaskOrganizer.registerOrganizer(); - mTaskOrganizer.setInterceptBackPressedOnTaskRoot(true); + // mTaskOrganizer.setInterceptBackPressedOnTaskRoot(true); return super.onInitialize(); } diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index eb59f0f59be1..da26930ca727 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -409,6 +409,11 @@ public final class SystemUiDeviceConfigFlags { */ public static final String BACK_GESTURE_SLOP_MULTIPLIER = "back_gesture_slop_multiplier"; + /** + * (long) Screenshot keychord delay (how long the buttons must be pressed), in ms + */ + public static final String SCREENSHOT_KEYCHORD_DELAY = "screenshot_keychord_delay"; + private SystemUiDeviceConfigFlags() { } } diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index a50a52219c74..3b5fecfc600a 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -113,6 +113,14 @@ public abstract class FileSystemProvider extends DocumentsProvider { // Default is no-op } + /** + * Callback indicating that the given document has been deleted or moved. This gives + * the provider a hook to revoke the uri permissions. + */ + protected void onDocIdDeleted(String docId) { + // Default is no-op + } + @Override public boolean onCreate() { throw new UnsupportedOperationException( @@ -283,6 +291,7 @@ public abstract class FileSystemProvider extends DocumentsProvider { final String afterDocId = getDocIdForFile(after); onDocIdChanged(docId); + onDocIdDeleted(docId); onDocIdChanged(afterDocId); final File afterVisibleFile = getFileForDocId(afterDocId, true); @@ -312,6 +321,7 @@ public abstract class FileSystemProvider extends DocumentsProvider { final String docId = getDocIdForFile(after); onDocIdChanged(sourceDocumentId); + onDocIdDeleted(sourceDocumentId); onDocIdChanged(docId); moveInMediaStore(visibleFileBefore, getFileForDocId(docId, true)); @@ -343,6 +353,7 @@ public abstract class FileSystemProvider extends DocumentsProvider { } onDocIdChanged(docId); + onDocIdDeleted(docId); removeFromMediaStore(visibleFile); } diff --git a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java new file mode 100644 index 000000000000..8a4eb4a9ca71 --- /dev/null +++ b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java @@ -0,0 +1,397 @@ +/* + * 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.internal.protolog; + +import static com.android.internal.protolog.ProtoLogFileProto.LOG; +import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER; +import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_H; +import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_L; +import static com.android.internal.protolog.ProtoLogFileProto.REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS; +import static com.android.internal.protolog.ProtoLogFileProto.VERSION; +import static com.android.internal.protolog.ProtoLogMessage.BOOLEAN_PARAMS; +import static com.android.internal.protolog.ProtoLogMessage.DOUBLE_PARAMS; +import static com.android.internal.protolog.ProtoLogMessage.ELAPSED_REALTIME_NANOS; +import static com.android.internal.protolog.ProtoLogMessage.MESSAGE_HASH; +import static com.android.internal.protolog.ProtoLogMessage.SINT64_PARAMS; +import static com.android.internal.protolog.ProtoLogMessage.STR_PARAMS; + +import android.annotation.Nullable; +import android.os.ShellCommand; +import android.os.SystemClock; +import android.util.Slog; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.common.IProtoLogGroup; +import com.android.internal.protolog.common.LogDataType; +import com.android.internal.util.TraceBuffer; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.IllegalFormatConversionException; +import java.util.TreeMap; +import java.util.stream.Collectors; + + +/** + * A service for the ProtoLog logging system. + */ +public class BaseProtoLogImpl { + protected static final TreeMap<String, IProtoLogGroup> LOG_GROUPS = new TreeMap<>(); + + /** + * A runnable to update the cached output of {@link #isEnabled}. + * + * Must be invoked after every action that could change the result of {@link #isEnabled}, eg. + * starting / stopping proto log, or enabling / disabling log groups. + */ + public static Runnable sCacheUpdater = () -> { }; + + protected static void addLogGroupEnum(IProtoLogGroup[] config) { + for (IProtoLogGroup group : config) { + LOG_GROUPS.put(group.name(), group); + } + } + + private static final String TAG = "ProtoLog"; + private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; + static final String PROTOLOG_VERSION = "1.0.0"; + + private final File mLogFile; + private final String mViewerConfigFilename; + private final TraceBuffer mBuffer; + protected final ProtoLogViewerConfigReader mViewerConfig; + + private boolean mProtoLogEnabled; + private boolean mProtoLogEnabledLockFree; + private final Object mProtoLogEnabledLock = new Object(); + + @VisibleForTesting + public enum LogLevel { + DEBUG, VERBOSE, INFO, WARN, ERROR, WTF + } + + /** + * Main log method, do not call directly. + */ + @VisibleForTesting + public void log(LogLevel level, IProtoLogGroup group, int messageHash, int paramsMask, + @Nullable String messageString, Object[] args) { + if (group.isLogToProto()) { + logToProto(messageHash, paramsMask, args); + } + if (group.isLogToLogcat()) { + logToLogcat(group.getTag(), level, messageHash, messageString, args); + } + } + + private void logToLogcat(String tag, LogLevel level, int messageHash, + @Nullable String messageString, Object[] args) { + String message = null; + if (messageString == null) { + messageString = mViewerConfig.getViewerString(messageHash); + } + if (messageString != null) { + try { + message = String.format(messageString, args); + } catch (IllegalFormatConversionException ex) { + Slog.w(TAG, "Invalid ProtoLog format string.", ex); + } + } + if (message == null) { + StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE (" + messageHash + ")"); + for (Object o : args) { + builder.append(" ").append(o); + } + message = builder.toString(); + } + passToLogcat(tag, level, message); + } + + /** + * SLog wrapper. + */ + @VisibleForTesting + public void passToLogcat(String tag, LogLevel level, String message) { + switch (level) { + case DEBUG: + Slog.d(tag, message); + break; + case VERBOSE: + Slog.v(tag, message); + break; + case INFO: + Slog.i(tag, message); + break; + case WARN: + Slog.w(tag, message); + break; + case ERROR: + Slog.e(tag, message); + break; + case WTF: + Slog.wtf(tag, message); + break; + } + } + + private void logToProto(int messageHash, int paramsMask, Object[] args) { + if (!isProtoEnabled()) { + return; + } + try { + ProtoOutputStream os = new ProtoOutputStream(); + long token = os.start(LOG); + os.write(MESSAGE_HASH, messageHash); + os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos()); + + if (args != null) { + int argIndex = 0; + ArrayList<Long> longParams = new ArrayList<>(); + ArrayList<Double> doubleParams = new ArrayList<>(); + ArrayList<Boolean> booleanParams = new ArrayList<>(); + for (Object o : args) { + int type = LogDataType.bitmaskToLogDataType(paramsMask, argIndex); + try { + switch (type) { + case LogDataType.STRING: + os.write(STR_PARAMS, o.toString()); + break; + case LogDataType.LONG: + longParams.add(((Number) o).longValue()); + break; + case LogDataType.DOUBLE: + doubleParams.add(((Number) o).doubleValue()); + break; + case LogDataType.BOOLEAN: + booleanParams.add((boolean) o); + break; + } + } catch (ClassCastException ex) { + // Should not happen unless there is an error in the ProtoLogTool. + os.write(STR_PARAMS, "(INVALID PARAMS_MASK) " + o.toString()); + Slog.e(TAG, "Invalid ProtoLog paramsMask", ex); + } + argIndex++; + } + if (longParams.size() > 0) { + os.writePackedSInt64(SINT64_PARAMS, + longParams.stream().mapToLong(i -> i).toArray()); + } + if (doubleParams.size() > 0) { + os.writePackedDouble(DOUBLE_PARAMS, + doubleParams.stream().mapToDouble(i -> i).toArray()); + } + if (booleanParams.size() > 0) { + boolean[] arr = new boolean[booleanParams.size()]; + for (int i = 0; i < booleanParams.size(); i++) { + arr[i] = booleanParams.get(i); + } + os.writePackedBool(BOOLEAN_PARAMS, arr); + } + } + os.end(token); + mBuffer.add(os); + } catch (Exception e) { + Slog.e(TAG, "Exception while logging to proto", e); + } + } + + public BaseProtoLogImpl(File file, String viewerConfigFilename, int bufferCapacity, + ProtoLogViewerConfigReader viewerConfig) { + mLogFile = file; + mBuffer = new TraceBuffer(bufferCapacity); + mViewerConfigFilename = viewerConfigFilename; + mViewerConfig = viewerConfig; + } + + /** + * Starts the logging a circular proto buffer. + * + * @param pw Print writer + */ + public void startProtoLog(@Nullable PrintWriter pw) { + if (isProtoEnabled()) { + return; + } + synchronized (mProtoLogEnabledLock) { + logAndPrintln(pw, "Start logging to " + mLogFile + "."); + mBuffer.resetBuffer(); + mProtoLogEnabled = true; + mProtoLogEnabledLockFree = true; + } + sCacheUpdater.run(); + } + + /** + * Stops logging to proto. + * + * @param pw Print writer + * @param writeToFile If the current buffer should be written to disk or not + */ + public void stopProtoLog(@Nullable PrintWriter pw, boolean writeToFile) { + if (!isProtoEnabled()) { + return; + } + synchronized (mProtoLogEnabledLock) { + logAndPrintln(pw, "Stop logging to " + mLogFile + ". Waiting for log to flush."); + mProtoLogEnabled = mProtoLogEnabledLockFree = false; + if (writeToFile) { + writeProtoLogToFileLocked(); + logAndPrintln(pw, "Log written to " + mLogFile + "."); + } + if (mProtoLogEnabled) { + logAndPrintln(pw, "ERROR: logging was re-enabled while waiting for flush."); + throw new IllegalStateException("logging enabled while waiting for flush."); + } + } + sCacheUpdater.run(); + } + + /** + * Returns {@code true} iff logging to proto is enabled. + */ + public boolean isProtoEnabled() { + return mProtoLogEnabledLockFree; + } + + protected int setLogging(boolean setTextLogging, boolean value, PrintWriter pw, + String... groups) { + for (int i = 0; i < groups.length; i++) { + String group = groups[i]; + IProtoLogGroup g = LOG_GROUPS.get(group); + if (g != null) { + System.out.println("G: "+ g); + if (setTextLogging) { + g.setLogToLogcat(value); + } else { + g.setLogToProto(value); + } + } else { + logAndPrintln(pw, "No IProtoLogGroup named " + group); + return -1; + } + } + sCacheUpdater.run(); + return 0; + } + + private int unknownCommand(PrintWriter pw) { + pw.println("Unknown command"); + pw.println("Window manager logging options:"); + pw.println(" start: Start proto logging"); + pw.println(" stop: Stop proto logging"); + pw.println(" enable [group...]: Enable proto logging for given groups"); + pw.println(" disable [group...]: Disable proto logging for given groups"); + pw.println(" enable-text [group...]: Enable logcat logging for given groups"); + pw.println(" disable-text [group...]: Disable logcat logging for given groups"); + return -1; + } + + /** + * Responds to a shell command. + */ + public int onShellCommand(ShellCommand shell) { + PrintWriter pw = shell.getOutPrintWriter(); + String cmd = shell.getNextArg(); + if (cmd == null) { + return unknownCommand(pw); + } + ArrayList<String> args = new ArrayList<>(); + String arg; + while ((arg = shell.getNextArg()) != null) { + args.add(arg); + } + String[] groups = args.toArray(new String[args.size()]); + switch (cmd) { + case "start": + startProtoLog(pw); + return 0; + case "stop": + stopProtoLog(pw, true); + return 0; + case "status": + logAndPrintln(pw, getStatus()); + return 0; + case "enable": + return setLogging(false, true, pw, groups); + case "enable-text": + mViewerConfig.loadViewerConfig(pw, mViewerConfigFilename); + return setLogging(true, true, pw, groups); + case "disable": + return setLogging(false, false, pw, groups); + case "disable-text": + return setLogging(true, false, pw, groups); + default: + return unknownCommand(pw); + } + } + + /** + * Returns a human-readable ProtoLog status text. + */ + public String getStatus() { + return "ProtoLog status: " + + ((isProtoEnabled()) ? "Enabled" : "Disabled") + + "\nEnabled log groups: \n Proto: " + + LOG_GROUPS.values().stream().filter( + it -> it.isEnabled() && it.isLogToProto()) + .map(IProtoLogGroup::name).collect(Collectors.joining(" ")) + + "\n Logcat: " + + LOG_GROUPS.values().stream().filter( + it -> it.isEnabled() && it.isLogToLogcat()) + .map(IProtoLogGroup::name).collect(Collectors.joining(" ")) + + "\nLogging definitions loaded: " + mViewerConfig.knownViewerStringsNumber(); + } + + /** + * Writes the log buffer to a new file for the bugreport. + * + * This method is synchronized with {@code #startProtoLog(PrintWriter)} and + * {@link #stopProtoLog(PrintWriter, boolean)}. + */ + public void writeProtoLogToFile() { + synchronized (mProtoLogEnabledLock) { + writeProtoLogToFileLocked(); + } + } + + private void writeProtoLogToFileLocked() { + try { + long offset = + (System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000)); + ProtoOutputStream proto = new ProtoOutputStream(); + proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); + proto.write(VERSION, PROTOLOG_VERSION); + proto.write(REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS, offset); + mBuffer.writeTraceToFile(mLogFile, proto); + } catch (IOException e) { + Slog.e(TAG, "Unable to write buffer to file", e); + } + } + + static void logAndPrintln(@Nullable PrintWriter pw, String msg) { + Slog.i(TAG, msg); + if (pw != null) { + pw.println(msg); + pw.flush(); + } + } +} + diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java index 73d148c1f233..9f7436a13bdc 100644 --- a/core/java/com/android/internal/protolog/ProtoLogGroup.java +++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java @@ -36,7 +36,18 @@ public enum ProtoLogGroup implements IProtoLogGroup { Consts.TAG_WM), WM_DEBUG_ADD_REMOVE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM), - WM_DEBUG_FOCUS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM), + WM_DEBUG_CONFIGURATION(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM), + WM_DEBUG_SWITCH(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM), + WM_DEBUG_CONTAINERS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM), + WM_DEBUG_FOCUS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM), + WM_DEBUG_IMMERSIVE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM), + WM_DEBUG_LOCKTASK(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM), WM_DEBUG_STARTING_WINDOW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM), WM_SHOW_TRANSACTIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, @@ -59,6 +70,8 @@ public enum ProtoLogGroup implements IProtoLogGroup { Consts.TAG_WM), WM_DEBUG_IME(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM), + WM_DEBUG_WINDOW_ORGANIZER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM), TEST_GROUP(true, true, false, "WindowManagetProtoLogTest"); private final boolean mEnabled; diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java index 6874f10e6abc..10224a4b9db6 100644 --- a/core/java/com/android/internal/protolog/ProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java @@ -16,58 +16,22 @@ package com.android.internal.protolog; -import static com.android.internal.protolog.ProtoLogFileProto.LOG; -import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER; -import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_H; -import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_L; -import static com.android.internal.protolog.ProtoLogFileProto.REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS; -import static com.android.internal.protolog.ProtoLogFileProto.VERSION; -import static com.android.internal.protolog.ProtoLogMessage.BOOLEAN_PARAMS; -import static com.android.internal.protolog.ProtoLogMessage.DOUBLE_PARAMS; -import static com.android.internal.protolog.ProtoLogMessage.ELAPSED_REALTIME_NANOS; -import static com.android.internal.protolog.ProtoLogMessage.MESSAGE_HASH; -import static com.android.internal.protolog.ProtoLogMessage.SINT64_PARAMS; -import static com.android.internal.protolog.ProtoLogMessage.STR_PARAMS; - import android.annotation.Nullable; -import android.os.ShellCommand; -import android.os.SystemClock; -import android.util.Slog; -import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.IProtoLogGroup; -import com.android.internal.protolog.common.LogDataType; -import com.android.internal.util.TraceBuffer; import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.IllegalFormatConversionException; -import java.util.TreeMap; -import java.util.stream.Collectors; - /** * A service for the ProtoLog logging system. */ -public class ProtoLogImpl { - private static final TreeMap<String, IProtoLogGroup> LOG_GROUPS = new TreeMap<>(); - - /** - * A runnable to update the cached output of {@link #isEnabled}. - * - * Must be invoked after every action that could change the result of {@link #isEnabled}, eg. - * starting / stopping proto log, or enabling / disabling log groups. - */ - public static Runnable sCacheUpdater = () -> { }; +public class ProtoLogImpl extends BaseProtoLogImpl { + private static final int BUFFER_CAPACITY = 1024 * 1024; + private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.pb"; + private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz"; - private static void addLogGroupEnum(IProtoLogGroup[] config) { - for (IProtoLogGroup group : config) { - LOG_GROUPS.put(group.name(), group); - } - } + private static ProtoLogImpl sServiceInstance = null; static { addLogGroupEnum(ProtoLogGroup.values()); @@ -124,30 +88,13 @@ public class ProtoLogImpl { || (group.isLogToProto() && getSingleInstance().isProtoEnabled()); } - private static final int BUFFER_CAPACITY = 1024 * 1024; - private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.pb"; - private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz"; - private static final String TAG = "ProtoLog"; - private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; - static final String PROTOLOG_VERSION = "1.0.0"; - - private final File mLogFile; - private final TraceBuffer mBuffer; - private final ProtoLogViewerConfigReader mViewerConfig; - - private boolean mProtoLogEnabled; - private boolean mProtoLogEnabledLockFree; - private final Object mProtoLogEnabledLock = new Object(); - - private static ProtoLogImpl sServiceInstance = null; - /** * Returns the single instance of the ProtoLogImpl singleton class. */ public static synchronized ProtoLogImpl getSingleInstance() { if (sServiceInstance == null) { - sServiceInstance = new ProtoLogImpl(new File(LOG_FILENAME), BUFFER_CAPACITY, - new ProtoLogViewerConfigReader()); + sServiceInstance = new ProtoLogImpl( + new File(LOG_FILENAME), BUFFER_CAPACITY, new ProtoLogViewerConfigReader()); } return sServiceInstance; } @@ -157,307 +104,9 @@ public class ProtoLogImpl { sServiceInstance = instance; } - @VisibleForTesting - public enum LogLevel { - DEBUG, VERBOSE, INFO, WARN, ERROR, WTF - } - - /** - * Main log method, do not call directly. - */ - @VisibleForTesting - public void log(LogLevel level, IProtoLogGroup group, int messageHash, int paramsMask, - @Nullable String messageString, Object[] args) { - if (group.isLogToProto()) { - logToProto(messageHash, paramsMask, args); - } - if (group.isLogToLogcat()) { - logToLogcat(group.getTag(), level, messageHash, messageString, args); - } - } - - private void logToLogcat(String tag, LogLevel level, int messageHash, - @Nullable String messageString, Object[] args) { - String message = null; - if (messageString == null) { - messageString = mViewerConfig.getViewerString(messageHash); - } - if (messageString != null) { - try { - message = String.format(messageString, args); - } catch (IllegalFormatConversionException ex) { - Slog.w(TAG, "Invalid ProtoLog format string.", ex); - } - } - if (message == null) { - StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE (" + messageHash + ")"); - for (Object o : args) { - builder.append(" ").append(o); - } - message = builder.toString(); - } - passToLogcat(tag, level, message); - } - - /** - * SLog wrapper. - */ - @VisibleForTesting - public void passToLogcat(String tag, LogLevel level, String message) { - switch (level) { - case DEBUG: - Slog.d(tag, message); - break; - case VERBOSE: - Slog.v(tag, message); - break; - case INFO: - Slog.i(tag, message); - break; - case WARN: - Slog.w(tag, message); - break; - case ERROR: - Slog.e(tag, message); - break; - case WTF: - Slog.wtf(tag, message); - break; - } - } - - private void logToProto(int messageHash, int paramsMask, Object[] args) { - if (!isProtoEnabled()) { - return; - } - try { - ProtoOutputStream os = new ProtoOutputStream(); - long token = os.start(LOG); - os.write(MESSAGE_HASH, messageHash); - os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos()); - - if (args != null) { - int argIndex = 0; - ArrayList<Long> longParams = new ArrayList<>(); - ArrayList<Double> doubleParams = new ArrayList<>(); - ArrayList<Boolean> booleanParams = new ArrayList<>(); - for (Object o : args) { - int type = LogDataType.bitmaskToLogDataType(paramsMask, argIndex); - try { - switch (type) { - case LogDataType.STRING: - os.write(STR_PARAMS, o.toString()); - break; - case LogDataType.LONG: - longParams.add(((Number) o).longValue()); - break; - case LogDataType.DOUBLE: - doubleParams.add(((Number) o).doubleValue()); - break; - case LogDataType.BOOLEAN: - booleanParams.add((boolean) o); - break; - } - } catch (ClassCastException ex) { - // Should not happen unless there is an error in the ProtoLogTool. - os.write(STR_PARAMS, "(INVALID PARAMS_MASK) " + o.toString()); - Slog.e(TAG, "Invalid ProtoLog paramsMask", ex); - } - argIndex++; - } - if (longParams.size() > 0) { - os.writePackedSInt64(SINT64_PARAMS, - longParams.stream().mapToLong(i -> i).toArray()); - } - if (doubleParams.size() > 0) { - os.writePackedDouble(DOUBLE_PARAMS, - doubleParams.stream().mapToDouble(i -> i).toArray()); - } - if (booleanParams.size() > 0) { - boolean[] arr = new boolean[booleanParams.size()]; - for (int i = 0; i < booleanParams.size(); i++) { - arr[i] = booleanParams.get(i); - } - os.writePackedBool(BOOLEAN_PARAMS, arr); - } - } - os.end(token); - mBuffer.add(os); - } catch (Exception e) { - Slog.e(TAG, "Exception while logging to proto", e); - } - } - - public ProtoLogImpl(File file, int bufferCapacity, ProtoLogViewerConfigReader viewerConfig) { - mLogFile = file; - mBuffer = new TraceBuffer(bufferCapacity); - mViewerConfig = viewerConfig; - } - - /** - * Starts the logging a circular proto buffer. - * - * @param pw Print writer - */ - public void startProtoLog(@Nullable PrintWriter pw) { - if (isProtoEnabled()) { - return; - } - synchronized (mProtoLogEnabledLock) { - logAndPrintln(pw, "Start logging to " + mLogFile + "."); - mBuffer.resetBuffer(); - mProtoLogEnabled = true; - mProtoLogEnabledLockFree = true; - } - sCacheUpdater.run(); - } - - /** - * Stops logging to proto. - * - * @param pw Print writer - * @param writeToFile If the current buffer should be written to disk or not - */ - public void stopProtoLog(@Nullable PrintWriter pw, boolean writeToFile) { - if (!isProtoEnabled()) { - return; - } - synchronized (mProtoLogEnabledLock) { - logAndPrintln(pw, "Stop logging to " + mLogFile + ". Waiting for log to flush."); - mProtoLogEnabled = mProtoLogEnabledLockFree = false; - if (writeToFile) { - writeProtoLogToFileLocked(); - logAndPrintln(pw, "Log written to " + mLogFile + "."); - } - if (mProtoLogEnabled) { - logAndPrintln(pw, "ERROR: logging was re-enabled while waiting for flush."); - throw new IllegalStateException("logging enabled while waiting for flush."); - } - } - sCacheUpdater.run(); - } - - /** - * Returns {@code true} iff logging to proto is enabled. - */ - public boolean isProtoEnabled() { - return mProtoLogEnabledLockFree; - } - - private int setLogging(ShellCommand shell, boolean setTextLogging, boolean value) { - String group; - while ((group = shell.getNextArg()) != null) { - IProtoLogGroup g = LOG_GROUPS.get(group); - if (g != null) { - if (setTextLogging) { - g.setLogToLogcat(value); - } else { - g.setLogToProto(value); - } - } else { - logAndPrintln(shell.getOutPrintWriter(), "No IProtoLogGroup named " + group); - return -1; - } - } - sCacheUpdater.run(); - return 0; - } - - private int unknownCommand(PrintWriter pw) { - pw.println("Unknown command"); - pw.println("Window manager logging options:"); - pw.println(" start: Start proto logging"); - pw.println(" stop: Stop proto logging"); - pw.println(" enable [group...]: Enable proto logging for given groups"); - pw.println(" disable [group...]: Disable proto logging for given groups"); - pw.println(" enable-text [group...]: Enable logcat logging for given groups"); - pw.println(" disable-text [group...]: Disable logcat logging for given groups"); - return -1; - } - - /** - * Responds to a shell command. - */ - public int onShellCommand(ShellCommand shell) { - PrintWriter pw = shell.getOutPrintWriter(); - String cmd = shell.getNextArg(); - if (cmd == null) { - return unknownCommand(pw); - } - switch (cmd) { - case "start": - startProtoLog(pw); - return 0; - case "stop": - stopProtoLog(pw, true); - return 0; - case "status": - logAndPrintln(pw, getStatus()); - return 0; - case "enable": - return setLogging(shell, false, true); - case "enable-text": - mViewerConfig.loadViewerConfig(pw, VIEWER_CONFIG_FILENAME); - return setLogging(shell, true, true); - case "disable": - return setLogging(shell, false, false); - case "disable-text": - return setLogging(shell, true, false); - default: - return unknownCommand(pw); - } - } - - /** - * Returns a human-readable ProtoLog status text. - */ - public String getStatus() { - return "ProtoLog status: " - + ((isProtoEnabled()) ? "Enabled" : "Disabled") - + "\nEnabled log groups: \n Proto: " - + LOG_GROUPS.values().stream().filter( - it -> it.isEnabled() && it.isLogToProto()) - .map(IProtoLogGroup::name).collect(Collectors.joining(" ")) - + "\n Logcat: " - + LOG_GROUPS.values().stream().filter( - it -> it.isEnabled() && it.isLogToLogcat()) - .map(IProtoLogGroup::name).collect(Collectors.joining(" ")) - + "\nLogging definitions loaded: " + mViewerConfig.knownViewerStringsNumber(); - } - - /** - * Writes the log buffer to a new file for the bugreport. - * - * This method is synchronized with {@code #startProtoLog(PrintWriter)} and - * {@link #stopProtoLog(PrintWriter, boolean)}. - */ - public void writeProtoLogToFile() { - synchronized (mProtoLogEnabledLock) { - writeProtoLogToFileLocked(); - } - } - - private void writeProtoLogToFileLocked() { - try { - long offset = - (System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000)); - ProtoOutputStream proto = new ProtoOutputStream(); - proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); - proto.write(VERSION, PROTOLOG_VERSION); - proto.write(REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS, offset); - mBuffer.writeTraceToFile(mLogFile, proto); - } catch (IOException e) { - Slog.e(TAG, "Unable to write buffer to file", e); - } - } - - - static void logAndPrintln(@Nullable PrintWriter pw, String msg) { - Slog.i(TAG, msg); - if (pw != null) { - pw.println(msg); - pw.flush(); - } + public ProtoLogImpl(File logFile, int bufferCapacity, + ProtoLogViewerConfigReader viewConfigReader) { + super(logFile, VIEWER_CONFIG_FILENAME, bufferCapacity, viewConfigReader); } } diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java index e381d30da524..aa30a7723ad9 100644 --- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java +++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java @@ -16,6 +16,9 @@ package com.android.internal.protolog; +import android.annotation.Nullable; +import android.util.Slog; + import org.json.JSONException; import org.json.JSONObject; @@ -23,6 +26,7 @@ import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.Iterator; @@ -34,6 +38,7 @@ import java.util.zip.GZIPInputStream; * Handles loading and parsing of ProtoLog viewer configuration. */ public class ProtoLogViewerConfigReader { + private static final String TAG = "ProtoLogViewerConfigReader"; private Map<Integer, String> mLogMessageMap = null; /** Returns message format string for its hash or null if unavailable. */ @@ -49,48 +54,54 @@ public class ProtoLogViewerConfigReader { * Reads the specified viewer configuration file. Does nothing if the config is already loaded. */ public synchronized void loadViewerConfig(PrintWriter pw, String viewerConfigFilename) { - if (mLogMessageMap != null) { - return; - } try { - InputStreamReader config = new InputStreamReader( - new GZIPInputStream(new FileInputStream(viewerConfigFilename))); - BufferedReader reader = new BufferedReader(config); - StringBuilder builder = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - builder.append(line).append('\n'); - } - reader.close(); - JSONObject json = new JSONObject(builder.toString()); - JSONObject messages = json.getJSONObject("messages"); - - mLogMessageMap = new TreeMap<>(); - Iterator it = messages.keys(); - while (it.hasNext()) { - String key = (String) it.next(); - try { - int hash = Integer.parseInt(key); - JSONObject val = messages.getJSONObject(key); - String msg = val.getString("message"); - mLogMessageMap.put(hash, msg); - } catch (NumberFormatException expected) { - // Not a messageHash - skip it - } - } - ProtoLogImpl.logAndPrintln(pw, "Loaded " + mLogMessageMap.size() + loadViewerConfig(new GZIPInputStream(new FileInputStream(viewerConfigFilename))); + logAndPrintln(pw, "Loaded " + mLogMessageMap.size() + " log definitions from " + viewerConfigFilename); } catch (FileNotFoundException e) { - ProtoLogImpl.logAndPrintln(pw, "Unable to load log definitions: File " + logAndPrintln(pw, "Unable to load log definitions: File " + viewerConfigFilename + " not found." + e); } catch (IOException e) { - ProtoLogImpl.logAndPrintln(pw, - "Unable to load log definitions: IOException while reading " + logAndPrintln(pw, "Unable to load log definitions: IOException while reading " + viewerConfigFilename + ". " + e); } catch (JSONException e) { - ProtoLogImpl.logAndPrintln(pw, - "Unable to load log definitions: JSON parsing exception while reading " - + viewerConfigFilename + ". " + e); + logAndPrintln(pw, "Unable to load log definitions: JSON parsing exception while reading " + + viewerConfigFilename + ". " + e); + } + } + + /** + * Reads the specified viewer configuration input stream. + * Does nothing if the config is already loaded. + */ + public synchronized void loadViewerConfig(InputStream viewerConfigInputStream) + throws IOException, JSONException { + if (mLogMessageMap != null) { + return; + } + InputStreamReader config = new InputStreamReader(viewerConfigInputStream); + BufferedReader reader = new BufferedReader(config); + StringBuilder builder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line).append('\n'); + } + reader.close(); + JSONObject json = new JSONObject(builder.toString()); + JSONObject messages = json.getJSONObject("messages"); + + mLogMessageMap = new TreeMap<>(); + Iterator it = messages.keys(); + while (it.hasNext()) { + String key = (String) it.next(); + try { + int hash = Integer.parseInt(key); + JSONObject val = messages.getJSONObject(key); + String msg = val.getString("message"); + mLogMessageMap.put(hash, msg); + } catch (NumberFormatException expected) { + // Not a messageHash - skip it + } } } @@ -103,4 +114,12 @@ public class ProtoLogViewerConfigReader { } return 0; } + + static void logAndPrintln(@Nullable PrintWriter pw, String msg) { + Slog.i(TAG, msg); + if (pw != null) { + pw.println(msg); + pw.flush(); + } + } } diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp index 9ad4cd9e9ae8..859b40afb7c4 100644 --- a/core/jni/android_hardware_camera2_CameraMetadata.cpp +++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp @@ -501,6 +501,15 @@ static void CameraMetadata_readFromParcel(JNIEnv *env, jclass thiz, jobject parc "Failed to read from parcel (error code %d)", err); return; } + + // Update vendor descriptor cache if necessary + auto vendorId = metadata->getVendorId(); + if ((vendorId != CAMERA_METADATA_INVALID_VENDOR_ID) && + !VendorTagDescriptorCache::isVendorCachePresent(vendorId)) { + ALOGW("%s: Tag vendor id missing or cache not initialized, trying to update!", + __FUNCTION__); + CameraMetadata_setupGlobalVendorTagDescriptor(env, thiz); + } } static void CameraMetadata_writeToParcel(JNIEnv *env, jclass thiz, jobject parcel, jlong ptr) { @@ -642,9 +651,7 @@ static jint CameraMetadata_getTypeFromTagLocal(JNIEnv *env, jclass thiz, jlong p CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(ptr); metadata_vendor_id_t vendorId = CAMERA_METADATA_INVALID_VENDOR_ID; if (metadata) { - const camera_metadata_t *metaBuffer = metadata->getAndLock(); - vendorId = get_camera_metadata_vendor_id(metaBuffer); - metadata->unlock(metaBuffer); + vendorId = metadata->getVendorId(); } int tagType = get_local_camera_metadata_tag_type_vendor_id(tag, vendorId); @@ -673,9 +680,7 @@ static jint CameraMetadata_getTagFromKeyLocal(JNIEnv *env, jclass thiz, jlong pt if (metadata) { sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache(); if (cache.get()) { - const camera_metadata_t *metaBuffer = metadata->getAndLock(); - metadata_vendor_id_t vendorId = get_camera_metadata_vendor_id(metaBuffer); - metadata->unlock(metaBuffer); + auto vendorId = metadata->getVendorId(); cache->getVendorTagDescriptor(vendorId, &vTags); } } @@ -703,10 +708,8 @@ static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jclass thiz, jlong p CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, ptr); if (metadata == NULL) return NULL; - const camera_metadata_t *metaBuffer = metadata->getAndLock(); - vendorId = get_camera_metadata_vendor_id(metaBuffer); + vendorId = metadata->getVendorId(); cache->getVendorTagDescriptor(vendorId, &vTags); - metadata->unlock(metaBuffer); if (vTags.get() == nullptr) { return nullptr; } diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 23af70a7cc18..cbcbe7f11390 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -613,7 +613,7 @@ void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz, } // Do not change sched policy cgroup after boot complete. - rc = androidSetThreadPriority(pid, pri, !boot_completed); + rc = androidSetThreadPriorityAndPolicy(pid, pri, !boot_completed); if (rc != 0) { if (rc == INVALID_OPERATION) { signalExceptionForPriorityError(env, errno, pid); diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 6eb89040998a..6212bcbbc05f 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2738,4 +2738,14 @@ enum PageId { // CATEGORY: SETTINGS // OS: S SETTINGS_COLUMBUS = 1848; + + // OPEN: Settings > Accessibility > Magnification > Settings > Magnification area > Magnification switch shortcut dialog + // CATEGORY: SETTINGS + // OS: S + DIALOG_MAGNIFICATION_SWITCH_SHORTCUT = 1849; + + // OPEN: Settings > Network & internet > Adaptive connectivity + // CATEGORY: SETTINGS + // OS: R QPR + ADAPTIVE_CONNECTIVITY_CATEGORY = 1850; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 57c1fcf7bfb4..cdcb24b6a247 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3094,7 +3094,8 @@ android:protectionLevel="signature" /> <!-- Allows an application to be the status bar. Currently used only by SystemUI.apk - @hide --> + @hide + @SystemApi --> <permission android:name="android.permission.STATUS_BAR_SERVICE" android:protectionLevel="signature" /> diff --git a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java index 72391f4d7dec..db127c6cb9ed 100644 --- a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java +++ b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java @@ -22,6 +22,7 @@ import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSE import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; import org.junit.Test; @@ -31,11 +32,22 @@ public class TimeZoneCapabilitiesTest { @Test public void testEquals() { - TimeZoneCapabilities.Builder builder1 = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID) + TimeZoneConfiguration configuration1 = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + .setAutoDetectionEnabled(true) + .setGeoDetectionEnabled(true) + .build(); + TimeZoneConfiguration configuration2 = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + .setAutoDetectionEnabled(false) + .setGeoDetectionEnabled(false) + .build(); + + TimeZoneCapabilities.Builder builder1 = new TimeZoneCapabilities.Builder() + .setConfiguration(configuration1) .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) .setSuggestManualTimeZone(CAPABILITY_POSSESSED); - TimeZoneCapabilities.Builder builder2 = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID) + TimeZoneCapabilities.Builder builder2 = new TimeZoneCapabilities.Builder() + .setConfiguration(configuration1) .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) .setSuggestManualTimeZone(CAPABILITY_POSSESSED); @@ -45,6 +57,20 @@ public class TimeZoneCapabilitiesTest { assertEquals(one, two); } + builder2.setConfiguration(configuration2); + { + TimeZoneCapabilities one = builder1.build(); + TimeZoneCapabilities two = builder2.build(); + assertNotEquals(one, two); + } + + builder1.setConfiguration(configuration2); + { + TimeZoneCapabilities one = builder1.build(); + TimeZoneCapabilities two = builder2.build(); + assertEquals(one, two); + } + builder2.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED); { TimeZoneCapabilities one = builder1.build(); @@ -90,7 +116,12 @@ public class TimeZoneCapabilitiesTest { @Test public void testParcelable() { - TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID) + TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + .setAutoDetectionEnabled(true) + .setGeoDetectionEnabled(true) + .build(); + TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder() + .setConfiguration(configuration) .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) .setSuggestManualTimeZone(CAPABILITY_POSSESSED); @@ -105,4 +136,51 @@ public class TimeZoneCapabilitiesTest { builder.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED); assertRoundTripParcelable(builder.build()); } + + @Test + public void testApplyUpdate_permitted() { + TimeZoneConfiguration oldConfiguration = + new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + .setAutoDetectionEnabled(true) + .setGeoDetectionEnabled(true) + .build(); + TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder() + .setConfiguration(oldConfiguration) + .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) + .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) + .setSuggestManualTimeZone(CAPABILITY_POSSESSED) + .build(); + assertEquals(oldConfiguration, capabilities.getConfiguration()); + + TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + .setAutoDetectionEnabled(false) + .build(); + + TimeZoneConfiguration expected = new TimeZoneConfiguration.Builder(oldConfiguration) + .setAutoDetectionEnabled(false) + .build(); + assertEquals(expected, capabilities.applyUpdate(configChange)); + } + + @Test + public void testApplyUpdate_notPermitted() { + TimeZoneConfiguration oldConfiguration = + new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + .setAutoDetectionEnabled(true) + .setGeoDetectionEnabled(true) + .build(); + TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder() + .setConfiguration(oldConfiguration) + .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED) + .setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED) + .setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED) + .build(); + assertEquals(oldConfiguration, capabilities.getConfiguration()); + + TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + .setAutoDetectionEnabled(false) + .build(); + + assertNull(capabilities.applyUpdate(configChange)); + } } diff --git a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java index 00dc73ed269f..faf908de8d4a 100644 --- a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java +++ b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java @@ -27,11 +27,14 @@ import org.junit.Test; public class TimeZoneConfigurationTest { + private static final int ARBITRARY_USER_ID = 9876; + @Test public void testBuilder_copyConstructor() { - TimeZoneConfiguration.Builder builder1 = new TimeZoneConfiguration.Builder() - .setAutoDetectionEnabled(true) - .setGeoDetectionEnabled(true); + TimeZoneConfiguration.Builder builder1 = + new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + .setAutoDetectionEnabled(true) + .setGeoDetectionEnabled(true); TimeZoneConfiguration configuration1 = builder1.build(); TimeZoneConfiguration configuration2 = @@ -41,28 +44,28 @@ public class TimeZoneConfigurationTest { } @Test - public void testIsComplete() { - TimeZoneConfiguration.Builder builder = - new TimeZoneConfiguration.Builder(); - assertFalse(builder.build().isComplete()); - - builder.setAutoDetectionEnabled(true); - assertFalse(builder.build().isComplete()); + public void testIntrospectionMethods() { + TimeZoneConfiguration empty = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID).build(); + assertFalse(empty.isComplete()); + assertFalse(empty.hasSetting(TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED)); - builder.setGeoDetectionEnabled(true); - assertTrue(builder.build().isComplete()); + TimeZoneConfiguration completeConfig = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + .setAutoDetectionEnabled(true) + .setGeoDetectionEnabled(true) + .build(); + assertTrue(completeConfig.isComplete()); + assertTrue(completeConfig.hasSetting(TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED)); } @Test public void testBuilder_mergeProperties() { - TimeZoneConfiguration configuration1 = - new TimeZoneConfiguration.Builder() - .setAutoDetectionEnabled(true) - .build(); + TimeZoneConfiguration configuration1 = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + .setAutoDetectionEnabled(true) + .build(); { TimeZoneConfiguration mergedEmptyAnd1 = - new TimeZoneConfiguration.Builder() + new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) .mergeProperties(configuration1) .build(); assertEquals(configuration1, mergedEmptyAnd1); @@ -70,7 +73,7 @@ public class TimeZoneConfigurationTest { { TimeZoneConfiguration configuration2 = - new TimeZoneConfiguration.Builder() + new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) .setAutoDetectionEnabled(false) .build(); @@ -87,14 +90,22 @@ public class TimeZoneConfigurationTest { @Test public void testEquals() { TimeZoneConfiguration.Builder builder1 = - new TimeZoneConfiguration.Builder(); + new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID); { TimeZoneConfiguration one = builder1.build(); assertEquals(one, one); } + { + TimeZoneConfiguration.Builder differentUserBuilder = + new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID + 1); + TimeZoneConfiguration one = builder1.build(); + TimeZoneConfiguration two = differentUserBuilder.build(); + assertNotEquals(one, two); + } + TimeZoneConfiguration.Builder builder2 = - new TimeZoneConfiguration.Builder(); + new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID); { TimeZoneConfiguration one = builder1.build(); TimeZoneConfiguration two = builder2.build(); @@ -148,7 +159,7 @@ public class TimeZoneConfigurationTest { @Test public void testParcelable() { TimeZoneConfiguration.Builder builder = - new TimeZoneConfiguration.Builder(); + new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID); assertRoundTripParcelable(builder.build()); builder.setAutoDetectionEnabled(true); diff --git a/core/tests/coretests/src/android/text/OWNERS b/core/tests/coretests/src/android/text/OWNERS index a35c6042bf53..0b51b2d90b79 100644 --- a/core/tests/coretests/src/android/text/OWNERS +++ b/core/tests/coretests/src/android/text/OWNERS @@ -1,5 +1,4 @@ set noparent siyamed@google.com -nona@google.com -clarabayarri@google.com
\ No newline at end of file +nona@google.com
\ No newline at end of file diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk index c577eeffd488..fe7c944ebd30 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk @@ -29,6 +29,8 @@ LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp LOCAL_DEX_PREOPT := false +LOCAL_EMMA_INSTRUMENT := false + mainDexList:= \ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list @@ -60,6 +62,8 @@ LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp2 LOCAL_DEX_PREOPT := false +LOCAL_EMMA_INSTRUMENT := false + mainDexList2:= \ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk index da40940e92e9..3636c73ffc9c 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk @@ -33,6 +33,8 @@ LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex LOCAL_DEX_PREOPT := false +LOCAL_EMMA_INSTRUMENT := false + include $(BUILD_PACKAGE) $(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS) diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk index 665e22d5a0bc..67f1fa574c07 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk @@ -28,6 +28,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex LOCAL_DEX_PREOPT := false +LOCAL_EMMA_INSTRUMENT := false + mainDexList:= \ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk index c827fa80ebcd..33871e527820 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk @@ -28,6 +28,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex LOCAL_DEX_PREOPT := false +LOCAL_EMMA_INSTRUMENT := false + mainDexList:= \ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk index 3d6ad7d1aa57..1b267ee93cce 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk @@ -31,6 +31,8 @@ mainDexList:= \ LOCAL_DEX_PREOPT := false +LOCAL_EMMA_INSTRUMENT := false + LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex include $(BUILD_PACKAGE) diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 73296987adde..75eb7b64a444 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -13,6 +13,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-2121056984": { + "message": "%s", + "level": "WARN", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/LockTaskController.java" + }, "-2109936758": { "message": "removeAppToken make exiting: %s", "level": "VERBOSE", @@ -37,12 +43,24 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-2049725903": { + "message": "Task back pressed on root taskId=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskOrganizerController.java" + }, "-2039580386": { "message": "Attempted to add input method window with unknown token %s. Aborting.", "level": "WARN", "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-2029985709": { + "message": "setFocusedTask: taskId=%d", + "level": "DEBUG", + "group": "WM_DEBUG_FOCUS", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "-2024464438": { "message": "app-onAnimationFinished(): mOuter=%s", "level": "DEBUG", @@ -73,6 +91,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java" }, + "-1980468143": { + "message": "DisplayArea appeared name=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" + }, "-1976930686": { "message": "Attempted to add Accessibility overlay window with bad token %s. Aborting.", "level": "WARN", @@ -91,6 +115,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1939861963": { + "message": "Create root task displayId=%d winMode=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskOrganizerController.java" + }, "-1939358269": { "message": "mRecentScreenshotAnimator finish", "level": "DEBUG", @@ -115,6 +145,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1895337367": { + "message": "Delete root task display=%d winMode=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskOrganizerController.java" + }, "-1884933373": { "message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s", "level": "INFO", @@ -139,6 +175,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotation.java" }, + "-1868048288": { + "message": "Updating to new configuration after starting activity.", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityStarter.java" + }, "-1862269827": { "message": "applyAnimation: anim=%s transit=%s isEntrance=%b Callers=%s", "level": "VERBOSE", @@ -163,6 +205,24 @@ "group": "WM_DEBUG_RESIZE", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-1810446914": { + "message": "Trying to update display configuration for system\/invalid process.", + "level": "WARN", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, + "-1792633344": { + "message": "Register task organizer=%s uid=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskOrganizerController.java" + }, + "-1791031393": { + "message": "Ensuring correct configuration: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-1782453012": { "message": "Checking theme of starting window: 0x%x", "level": "VERBOSE", @@ -211,6 +271,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayContent.java" }, + "-1699018375": { + "message": "Adding activity %s to task %s callers: %s", + "level": "INFO", + "group": "WM_DEBUG_ADD_REMOVE", + "at": "com\/android\/server\/wm\/Task.java" + }, "-1698815688": { "message": "Resetting app token %s of replacing window marks.", "level": "DEBUG", @@ -229,12 +295,30 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1638958146": { + "message": "Removing activity %s from task=%s adding to task=%s Callers=%s", + "level": "INFO", + "group": "WM_DEBUG_ADD_REMOVE", + "at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java" + }, "-1632122349": { "message": "Changing surface while display frozen: %s", "level": "VERBOSE", "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1630752478": { + "message": "removeLockedTask: removed %s", + "level": "DEBUG", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/LockTaskController.java" + }, + "-1598452494": { + "message": "activityDestroyedLocked: r=%s", + "level": "DEBUG", + "group": "WM_DEBUG_CONTAINERS", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-1596995693": { "message": "startAnimation", "level": "DEBUG", @@ -307,6 +391,18 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "-1495062622": { + "message": "Can't report activity moved to display - client not running, activityRecord=%s, displayId=%d", + "level": "WARN", + "group": "WM_DEBUG_SWITCH", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, + "-1492881555": { + "message": "Starting activity when config will change = %b", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityStarter.java" + }, "-1471946192": { "message": "Marking app token %s with replacing child windows.", "level": "DEBUG", @@ -355,6 +451,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1364754753": { + "message": "Task vanished taskId=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskOrganizerController.java" + }, "-1352076759": { "message": "Removing app token: %s", "level": "VERBOSE", @@ -379,6 +481,12 @@ "group": "WM_DEBUG_IME", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-1305755880": { + "message": "Initial config: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "-1292329638": { "message": "Added starting %s: startingWindow=%s startingView=%s", "level": "VERBOSE", @@ -445,6 +553,12 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimation.java" }, + "-1155279885": { + "message": "Frontmost changed immersion: %s", + "level": "DEBUG", + "group": "WM_DEBUG_IMMERSIVE", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "-1144293044": { "message": "SURFACE SET FREEZE LAYER: %s", "level": "INFO", @@ -475,6 +589,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotation.java" }, + "-1115019498": { + "message": "Configuration & display unchanged in %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-1113134997": { "message": "Attempted to add application window with unknown token %s. Aborting.", "level": "WARN", @@ -559,12 +679,36 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-951939129": { + "message": "Unregister task organizer=%s uid=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskOrganizerController.java" + }, + "-930893991": { + "message": "Set sync ready, syncId=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/WindowOrganizerController.java" + }, + "-929676529": { + "message": "Configuration changes for %s, allChanges=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-928291778": { "message": "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s", "level": "VERBOSE", "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", "at": "com\/android\/server\/wm\/AppTransition.java" }, + "-927199900": { + "message": "Updating global configuration to: %s", + "level": "INFO", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "-916108501": { "message": "Adding %s to %s", "level": "VERBOSE", @@ -619,12 +763,24 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-846078709": { + "message": "Configuration doesn't matter in finishing %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-809771899": { "message": "findFocusedWindow: Reached focused app=%s", "level": "VERBOSE", "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/DisplayContent.java" }, + "-804217032": { + "message": "Skipping config check (will change): %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-793346159": { "message": "New transit into wallpaper: %s", "level": "VERBOSE", @@ -673,11 +829,17 @@ "group": "WM_DEBUG_SCREEN_ON", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-714291355": { - "message": "Losing delayed focus: %s", - "level": "INFO", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" + "-743431900": { + "message": "Configuration no differences in %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, + "-716565534": { + "message": "moveActivityStackToFront: unfocusable activity=%s", + "level": "DEBUG", + "group": "WM_DEBUG_FOCUS", + "at": "com\/android\/server\/wm\/ActivityRecord.java" }, "-694710814": { "message": "Pausing rotation during drag", @@ -739,6 +901,12 @@ "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/DisplayContent.java" }, + "-593535526": { + "message": "Binding proc %s with config %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/am\/ActivityManagerService.java" + }, "-583031528": { "message": "%s", "level": "INFO", @@ -757,6 +925,12 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-548282316": { + "message": "setLockTaskMode: Locking to %s Callers=%s", + "level": "WARN", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/LockTaskController.java" + }, "-547111355": { "message": "hideIme Control target: %s ", "level": "DEBUG", @@ -781,6 +955,18 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-503656156": { + "message": "Update process config of %s to new config %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, + "-497620140": { + "message": "Transaction ready, syncId=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/WindowOrganizerController.java" + }, "-496681057": { "message": "Attempted to get remove mode of a display that does not exist: %d", "level": "WARN", @@ -799,6 +985,12 @@ "group": "WM_SHOW_SURFACE_ALLOC", "at": "com\/android\/server\/wm\/WindowStateAnimator.java" }, + "-449118559": { + "message": "Trying to update display configuration for invalid process, pid=%d", + "level": "WARN", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "-445944810": { "message": "finish(%b): mCanceled=%b", "level": "DEBUG", @@ -835,6 +1027,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java" }, + "-401282500": { + "message": "destroyIfPossible: r=%s destroy returned removed=%s", + "level": "DEBUG", + "group": "WM_DEBUG_CONTAINERS", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-395922585": { "message": "InsetsSource setWin %s", "level": "DEBUG", @@ -907,18 +1105,42 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-317194205": { + "message": "clearLockedTasks: %s", + "level": "INFO", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/LockTaskController.java" + }, "-303497363": { "message": "reparent: moving activity=%s to task=%d at %d", "level": "INFO", "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-272719931": { + "message": "startLockTaskModeLocked: %s", + "level": "WARN", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, + "-260960989": { + "message": "Removing and adding activity %s to stack at top callers=%s", + "level": "INFO", + "group": "WM_DEBUG_ADD_REMOVE", + "at": "com\/android\/server\/wm\/Task.java" + }, "-251259736": { "message": "No longer freezing: %s", "level": "VERBOSE", "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-235225312": { + "message": "Skipping config check for initializing activity: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-198463978": { "message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b", "level": "VERBOSE", @@ -943,6 +1165,12 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimationController.java" }, + "-168799453": { + "message": "Allowing features %d:0x%s", + "level": "WARN", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "-167822951": { "message": "Attempted to add starting window to token with already existing starting window", "level": "WARN", @@ -979,6 +1207,12 @@ "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-90559682": { + "message": "Config is skipping already pausing %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-87705714": { "message": "findFocusedWindow: focusedApp=null using new focus @ %s", "level": "VERBOSE", @@ -1159,6 +1393,12 @@ "group": "WM_SHOW_SURFACE_ALLOC", "at": "com\/android\/server\/wm\/BlackFrame.java" }, + "174572959": { + "message": "DisplayArea info changed name=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" + }, "184362060": { "message": "screenshotTask(%d): mCanceled=%b", "level": "DEBUG", @@ -1201,6 +1441,12 @@ "group": "WM_DEBUG_KEEP_SCREEN_ON", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, + "232317536": { + "message": "Set intercept back pressed on root=%b", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskOrganizerController.java" + }, "241961619": { "message": "Adding %s to %s", "level": "VERBOSE", @@ -1219,6 +1465,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "251812577": { + "message": "Register display organizer=%s uid=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" + }, "254883724": { "message": "addWindowToken: Attempted to add binder token: %s for already created window token: %s displayId=%d", "level": "WARN", @@ -1267,6 +1519,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotation.java" }, + "302969511": { + "message": "Task info changed taskId=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskOrganizerController.java" + }, "302992539": { "message": "addAnimation(%s)", "level": "DEBUG", @@ -1303,6 +1561,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "355940361": { + "message": "Config is destroying non-running %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "371641947": { "message": "Window Manager Crash %s", "level": "WTF", @@ -1315,18 +1579,18 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "374506950": { + "message": "Reporting activity moved to display, activityRecord=%s, displayId=%d, config=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_SWITCH", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "374972436": { "message": "performEnableScreen: Waiting for anim complete", "level": "INFO", "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "385096046": { - "message": "Delaying loss of focus...", - "level": "INFO", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, "399841913": { "message": "SURFACE RECOVER DESTROY: %s", "level": "INFO", @@ -1375,6 +1639,12 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimation.java" }, + "487621047": { + "message": "DisplayArea vanished name=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" + }, "490877640": { "message": "onStackOrderChanged(): stack=%s", "level": "DEBUG", @@ -1411,6 +1681,12 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "556758086": { + "message": "Applying new update lock state '%s' for %s", + "level": "DEBUG", + "group": "WM_DEBUG_IMMERSIVE", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "557227556": { "message": "onAnimationFinished(): Notify animation finished:", "level": "DEBUG", @@ -1531,12 +1807,6 @@ "group": "WM_DEBUG_SCREEN_ON", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "676824470": { - "message": "Test completed successfully: %b %d %o %x %e %g %f %% %s.", - "level": "ERROR", - "group": "TEST_GROUP", - "at": "com\/android\/server\/wm\/ProtoLogGroup.java" - }, "685047360": { "message": "Resizing window %s", "level": "VERBOSE", @@ -1561,6 +1831,18 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "715749922": { + "message": "Allowlisting %d:%s", + "level": "WARN", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, + "736692676": { + "message": "Config is relaunching %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "745391677": { "message": " CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x \/ %s", "level": "INFO", @@ -1615,6 +1897,12 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimation.java" }, + "869266572": { + "message": "Removing activity %s from stack, reason= %s callers=%s", + "level": "INFO", + "group": "WM_DEBUG_ADD_REMOVE", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "873914452": { "message": "goodToGo()", "level": "DEBUG", @@ -1633,6 +1921,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "906215061": { + "message": "Apply window transaction, syncId=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/WindowOrganizerController.java" + }, "913494177": { "message": "removeAllWindowsIfPossible: removing win=%s", "level": "WARN", @@ -1645,12 +1939,30 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowStateAnimator.java" }, + "950074526": { + "message": "setLockTaskMode: Can't lock due to auth", + "level": "WARN", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/LockTaskController.java" + }, "954470154": { "message": "FORCED DISPLAY SCALING DISABLED", "level": "INFO", "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "956374481": { + "message": "removeLockedTask: task=%s last task, reverting locktask mode. Callers=%s", + "level": "DEBUG", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/LockTaskController.java" + }, + "969323241": { + "message": "Sending new config to %s, config: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "972354148": { "message": "\tcontainer=%s", "level": "DEBUG", @@ -1663,12 +1975,24 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1040675582": { + "message": "Can't report activity configuration update - client not running, activityRecord=%s", + "level": "WARN", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "1046922686": { "message": "requestScrollCapture: caught exception dispatching callback: %s", "level": "WARN", "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1049367566": { + "message": "Sending to proc %s new config %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/WindowProcessController.java" + }, "1051545910": { "message": "Exit animation finished in %s: remove=%b", "level": "VERBOSE", @@ -1681,6 +2005,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java" }, + "1088929964": { + "message": "onLockTaskPackagesUpdated: starting new locktask task=%s", + "level": "DEBUG", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/LockTaskController.java" + }, "1089714158": { "message": " FREEZE %s: DESTROY", "level": "INFO", @@ -1705,6 +2035,12 @@ "group": "WM_DEBUG_SCREEN_ON", "at": "com\/android\/server\/wm\/DisplayPolicy.java" }, + "1149424314": { + "message": "Unregister display organizer=%s uid=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" + }, "1160771501": { "message": "Resize reasons for w=%s: %s surfaceResized=%b configChanged=%b dragResizingChanged=%b reportOrientationChanged=%b", "level": "VERBOSE", @@ -1789,6 +2125,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1337596507": { + "message": "Sending to proc %s new compat %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/CompatModePackages.java" + }, "1346895820": { "message": "ScreenRotation still animating: type: %d\nmDisplayAnimator: %s\nmEnterBlackFrameAnimator: %s\nmRotateScreenAnimator: %s\nmScreenshotRotationAnimator: %s", "level": "VERBOSE", @@ -1801,6 +2143,12 @@ "group": "WM_DEBUG_FOCUS", "at": "com\/android\/server\/wm\/DisplayContent.java" }, + "1360551978": { + "message": "Trying to update display configuration for non-existing displayId=%d", + "level": "WARN", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "1364498663": { "message": "notifyAppResumed: wasStopped=%b %s", "level": "VERBOSE", @@ -1819,6 +2167,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/TaskDisplayArea.java" }, + "1401295262": { + "message": "Mode default, asking user", + "level": "WARN", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/LockTaskController.java" + }, "1401700824": { "message": "Window drawn win=%s", "level": "DEBUG", @@ -1927,6 +2281,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1522489371": { + "message": "moveActivityStackToFront: activity=%s", + "level": "DEBUG", + "group": "WM_DEBUG_FOCUS", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "1525976603": { "message": "cancelAnimation(): reason=%s", "level": "DEBUG", @@ -1951,6 +2311,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1576607724": { + "message": "Report configuration: %s %s %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "1577579529": { "message": "win=%s destroySurfaces: appStopped=%b win.mWindowRemovalAllowed=%b win.mRemoveOnExit=%b", "level": "ERROR", @@ -1981,6 +2347,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1635062046": { + "message": "Skipping config check invisible: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "1635462459": { "message": "onMovedByResize: Moving %s", "level": "DEBUG", @@ -2023,6 +2395,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1679569477": { + "message": "Configuration doesn't matter not running %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "1720229827": { "message": "Creating animation bounds layer", "level": "INFO", @@ -2077,12 +2455,30 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1789603530": { + "message": "Removing activity %s hasSavedState=%b stateNotNeeded=%s finishing=%b state=%s callers=%s", + "level": "INFO", + "group": "WM_DEBUG_ADD_REMOVE", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "1822843721": { "message": "Aborted starting %s: startingData=%s", "level": "VERBOSE", "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1824105730": { + "message": "setLockTaskAuth: task=%s mLockTaskAuth=%s", + "level": "DEBUG", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/Task.java" + }, + "1829094918": { + "message": "onLockTaskPackagesUpdated: removing %s mLockTaskAuth()=%s", + "level": "DEBUG", + "group": "WM_DEBUG_LOCKTASK", + "at": "com\/android\/server\/wm\/LockTaskController.java" + }, "1831008694": { "message": "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s surfaceInsets=%s", "level": "DEBUG", @@ -2131,6 +2527,12 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1918448345": { + "message": "Task appeared taskId=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskOrganizerController.java" + }, "1921821199": { "message": "Preserving %s until the new one is added", "level": "VERBOSE", @@ -2161,6 +2563,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/AppTransitionController.java" }, + "1975793405": { + "message": "setFocusedStack: stackId=%d", + "level": "DEBUG", + "group": "WM_DEBUG_FOCUS", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "1984470582": { "message": "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d", "level": "DEBUG", @@ -2173,6 +2581,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowAnimator.java" }, + "1995093920": { + "message": "Checking to restart %s: changed=0x%s, handles=0x%s, mLastReportedConfiguration=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "2016061474": { "message": "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d Callers=%s", "level": "VERBOSE", @@ -2191,6 +2605,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "2022322588": { + "message": "Adding activity %s to stack to task %s callers: %s", + "level": "INFO", + "group": "WM_DEBUG_ADD_REMOVE", + "at": "com\/android\/server\/wm\/Task.java" + }, "2022422429": { "message": "createAnimationAdapter(): container=%s", "level": "DEBUG", @@ -2269,6 +2689,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotation.java" }, + "2134999275": { + "message": "moveActivityStackToFront: already on top, activity=%s", + "level": "DEBUG", + "group": "WM_DEBUG_FOCUS", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "2137411379": { "message": "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b Callers=%s", "level": "VERBOSE", @@ -2277,9 +2703,6 @@ } }, "groups": { - "TEST_GROUP": { - "tag": "WindowManagetProtoLogTest" - }, "WM_DEBUG_ADD_REMOVE": { "tag": "WindowManager" }, @@ -2292,6 +2715,12 @@ "WM_DEBUG_BOOT": { "tag": "WindowManager" }, + "WM_DEBUG_CONFIGURATION": { + "tag": "WindowManager" + }, + "WM_DEBUG_CONTAINERS": { + "tag": "WindowManager" + }, "WM_DEBUG_DRAW": { "tag": "WindowManager" }, @@ -2304,9 +2733,15 @@ "WM_DEBUG_IME": { "tag": "WindowManager" }, + "WM_DEBUG_IMMERSIVE": { + "tag": "WindowManager" + }, "WM_DEBUG_KEEP_SCREEN_ON": { "tag": "WindowManager" }, + "WM_DEBUG_LOCKTASK": { + "tag": "WindowManager" + }, "WM_DEBUG_ORIENTATION": { "tag": "WindowManager" }, @@ -2325,9 +2760,15 @@ "WM_DEBUG_STARTING_WINDOW": { "tag": "WindowManager" }, + "WM_DEBUG_SWITCH": { + "tag": "WindowManager" + }, "WM_DEBUG_WINDOW_MOVEMENT": { "tag": "WindowManager" }, + "WM_DEBUG_WINDOW_ORGANIZER": { + "tag": "WindowManager" + }, "WM_ERROR": { "tag": "WindowManager" }, diff --git a/keystore/TEST_MAPPING b/keystore/TEST_MAPPING new file mode 100644 index 000000000000..0511967a229b --- /dev/null +++ b/keystore/TEST_MAPPING @@ -0,0 +1,74 @@ +{ + "presubmit": [ + { + "name": "CtsKeystoreTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.RequiresDevice" + }, + { + "exclude-filter": "android.keystore.cts.SignatureTest" + }, + { + "exclude-filter": "android.keystore.cts.RsaSignaturePerformanceTest" + }, + { + "exclude-filter": "android.keystore.cts.RsaKeyGenPerformanceTest" + }, + { + "exclude-filter": "android.keystore.cts.RsaCipherPerformanceTest" + }, + { + "exclude-filter": "android.keystore.cts.MacTest#testLargeMsgKat" + }, + { + "exclude-filter": "android.keystore.cts.KeyPairGeneratorTest" + }, + { + "exclude-filter": "android.keystore.cts.KeyGeneratorTest#testHmacKeySupportedSizes" + }, + { + "exclude-filter": "android.keystore.cts.HmacMacPerformanceTest" + }, + { + "exclude-filter": "android.keystore.cts.EcdsaSignaturePerformanceTest" + }, + { + "exclude-filter": "android.keystore.cts.EcKeyGenPerformanceTest" + }, + { + "exclude-filter": "android.keystore.cts.DesCipherPerformanceTest" + }, + { + "exclude-filter": "android.keystore.cts.CipherTest" + }, + { + "exclude-filter": "android.keystore.cts.AttestationPerformanceTest" + }, + { + "exclude-filter": "android.keystore.cts.AndroidKeyStoreTest" + }, + { + "exclude-filter": "android.keystore.cts.AesCipherPerformanceTest" + }, + { + "exclude-filter": "android.keystore.cts.AESCipherNistCavpKatTest" + }, + { + "exclude-filter": "android.keystore.cts.DESedeECBPKCS7PaddingCipherTest" + }, + { + "exclude-filter": "android.keystore.cts.DESedeECBNoPaddingCipherTest" + }, + { + "exclude-filter": "android.keystore.cts.DESedeECBPKCS7PaddingCipherTest" + } + ] + } + ], + "postsubmit": [ + { + "name": "CtsKeystoreTestCases" + } + ] +} diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 843b17703676..307b82e69506 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -12,14 +12,88 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Begin ProtoLog +java_library { + name: "wm_shell_protolog-groups", + srcs: [ + "src/com/android/wm/shell/protolog/ShellProtoLogGroup.java", + ":protolog-common-src", + ], +} + +filegroup { + name: "wm_shell-sources", + srcs: ["src/**/*.java"], + path: "src", +} + +genrule { + name: "wm_shell_protolog_src", + srcs: [ + ":wm_shell_protolog-groups", + ":wm_shell-sources", + ], + tools: ["protologtool"], + cmd: "$(location protologtool) transform-protolog-calls " + + "--protolog-class com.android.internal.protolog.common.ProtoLog " + + "--protolog-impl-class com.android.wm.shell.protolog.ShellProtoLogImpl " + + "--protolog-cache-class com.android.wm.shell.protolog.ShellProtoLogCache " + + "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " + + "--loggroups-jar $(location :wm_shell_protolog-groups) " + + "--output-srcjar $(out) " + + "$(locations :wm_shell-sources)", + out: ["wm_shell_protolog.srcjar"], +} + +genrule { + name: "generate-wm_shell_protolog.json", + srcs: [ + ":wm_shell_protolog-groups", + ":wm_shell-sources", + ], + tools: ["protologtool"], + cmd: "$(location protologtool) generate-viewer-config " + + "--protolog-class com.android.internal.protolog.common.ProtoLog " + + "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " + + "--loggroups-jar $(location :wm_shell_protolog-groups) " + + "--viewer-conf $(out) " + + "$(locations :wm_shell-sources)", + out: ["wm_shell_protolog.json"], +} + +filegroup { + name: "wm_shell_protolog.json", + srcs: ["res/raw/wm_shell_protolog.json"], +} + +genrule { + name: "checked-wm_shell_protolog.json", + srcs: [ + ":generate-wm_shell_protolog.json", + ":wm_shell_protolog.json", + ], + cmd: "cp $(location :generate-wm_shell_protolog.json) $(out) && " + + "{ ! (diff $(out) $(location :wm_shell_protolog.json) | grep -q '^<') || " + + "{ echo -e '\\n\\n################################################################\\n#\\n" + + "# ERROR: ProtoLog viewer config is stale. To update it, run:\\n#\\n" + + "# cp $(location :generate-wm_shell_protolog.json) " + + "$(location :wm_shell_protolog.json)\\n#\\n" + + "################################################################\\n\\n' >&2 && false; } }", + out: ["wm_shell_protolog.json"], +} +// End ProtoLog + android_library { name: "WindowManager-Shell", srcs: [ - "src/**/*.java", + ":wm_shell_protolog_src", "src/**/I*.aidl", ], resource_dirs: [ "res", ], + static_libs: [ + "protolog-lib", + ], manifest: "AndroidManifest.xml", -} +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml index 2e0a5e09e34f..2e0a5e09e34f 100644 --- a/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml +++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json new file mode 100644 index 000000000000..7242793580f9 --- /dev/null +++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json @@ -0,0 +1,46 @@ +{ + "version": "1.0.0", + "messages": { + "-1340279385": { + "message": "Remove listener=%s", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" + }, + "-880817403": { + "message": "Task vanished taskId=%d", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" + }, + "-460572385": { + "message": "Task appeared taskId=%d", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" + }, + "-242812822": { + "message": "Add listener for modes=%s listener=%s", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" + }, + "157713005": { + "message": "Task info changed taskId=%d", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" + }, + "980952660": { + "message": "Task root back pressed taskId=%d", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" + } + }, + "groups": { + "WM_SHELL_TASK_ORG": { + "tag": "WindowManagerShell" + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index 126374829a18..ea9576a511e9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -17,17 +17,20 @@ package com.android.wm.shell; import android.app.ActivityManager.RunningTaskInfo; +import android.app.WindowConfiguration; +import android.content.Context; import android.util.Log; import android.util.Pair; import android.util.SparseArray; -import android.util.SparseIntArray; -import android.view.Surface; import android.view.SurfaceControl; import android.window.TaskOrganizer; +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.protolog.ShellProtoLogImpl; + import java.util.ArrayList; -import java.util.LinkedList; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.Arrays; /** * Unified task organizer for all components in the shell. @@ -57,6 +60,8 @@ public class ShellTaskOrganizer extends TaskOrganizer { * Adds a listener for tasks in a specific windowing mode. */ public void addListener(TaskListener listener, int... windowingModes) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Add listener for modes=%s listener=%s", + Arrays.toString(windowingModes), listener); for (int winMode : windowingModes) { ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(winMode); if (listeners == null) { @@ -84,6 +89,7 @@ public class ShellTaskOrganizer extends TaskOrganizer { * Removes a registered listener. */ public void removeListener(TaskListener listener) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Remove listener=%s", listener); for (int i = 0; i < mListenersByWindowingMode.size(); i++) { mListenersByWindowingMode.valueAt(i).remove(listener); } @@ -91,6 +97,8 @@ public class ShellTaskOrganizer extends TaskOrganizer { @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task appeared taskId=%d", + taskInfo.taskId); mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, leash)); ArrayList<TaskListener> listeners = mListenersByWindowingMode.get( getWindowingMode(taskInfo)); @@ -103,6 +111,8 @@ public class ShellTaskOrganizer extends TaskOrganizer { @Override public void onTaskInfoChanged(RunningTaskInfo taskInfo) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task info changed taskId=%d", + taskInfo.taskId); Pair<RunningTaskInfo, SurfaceControl> data = mTasks.get(taskInfo.taskId); int winMode = getWindowingMode(taskInfo); int prevWinMode = getWindowingMode(data.first); @@ -134,6 +144,8 @@ public class ShellTaskOrganizer extends TaskOrganizer { @Override public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d", + taskInfo.taskId); ArrayList<TaskListener> listeners = mListenersByWindowingMode.get( getWindowingMode(taskInfo)); if (listeners != null) { @@ -145,6 +157,8 @@ public class ShellTaskOrganizer extends TaskOrganizer { @Override public void onTaskVanished(RunningTaskInfo taskInfo) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task vanished taskId=%d", + taskInfo.taskId); int prevWinMode = getWindowingMode(mTasks.get(taskInfo.taskId).first); mTasks.remove(taskInfo.taskId); ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(prevWinMode); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java new file mode 100644 index 000000000000..ae0975467e3f --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java @@ -0,0 +1,92 @@ +/* + * 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.protolog; + +import com.android.internal.protolog.common.IProtoLogGroup; + +/** + * Defines logging groups for ProtoLog. + * + * This file is used by the ProtoLogTool to generate optimized logging code. + */ +public enum ShellProtoLogGroup implements IProtoLogGroup { + WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM_SHELL), + TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest"); + + private final boolean mEnabled; + private volatile boolean mLogToProto; + private volatile boolean mLogToLogcat; + private final String mTag; + + /** + * @param enabled set to false to exclude all log statements for this group from + * compilation, + * they will not be available in runtime. + * @param logToProto enable binary logging for the group + * @param logToLogcat enable text logging for the group + * @param tag name of the source of the logged message + */ + ShellProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) { + this.mEnabled = enabled; + this.mLogToProto = logToProto; + this.mLogToLogcat = logToLogcat; + this.mTag = tag; + } + + @Override + public boolean isEnabled() { + return mEnabled; + } + + @Override + public boolean isLogToProto() { + return mLogToProto; + } + + @Override + public boolean isLogToLogcat() { + return mLogToLogcat; + } + + @Override + public boolean isLogToAny() { + return mLogToLogcat || mLogToProto; + } + + @Override + public String getTag() { + return mTag; + } + + @Override + public void setLogToProto(boolean logToProto) { + this.mLogToProto = logToProto; + } + + @Override + public void setLogToLogcat(boolean logToLogcat) { + this.mLogToLogcat = logToLogcat; + } + + private static class Consts { + private static final String TAG_WM_SHELL = "WindowManagerShell"; + + private static final boolean ENABLE_DEBUG = true; + private static final boolean ENABLE_LOG_TO_PROTO_DEBUG = true; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java new file mode 100644 index 000000000000..6a925e74e847 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java @@ -0,0 +1,138 @@ +/* + * 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.protolog; + +import android.annotation.Nullable; +import android.content.Context; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.BaseProtoLogImpl; +import com.android.internal.protolog.ProtoLogViewerConfigReader; +import com.android.internal.protolog.common.IProtoLogGroup; +import com.android.wm.shell.R; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; + +import org.json.JSONException; + + +/** + * A service for the ProtoLog logging system. + */ +public class ShellProtoLogImpl extends BaseProtoLogImpl { + private static final String TAG = "ProtoLogImpl"; + private static final int BUFFER_CAPACITY = 1024 * 1024; + // TODO: Get the right path for the proto log file when we initialize the shell components + private static final String LOG_FILENAME = new File("wm_shell_log.pb").getAbsolutePath(); + + private static ShellProtoLogImpl sServiceInstance = null; + + private final PrintWriter mSystemOutWriter; + + static { + addLogGroupEnum(ShellProtoLogGroup.values()); + } + + /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */ + public static void d(IProtoLogGroup group, int messageHash, int paramsMask, + @Nullable String messageString, + Object... args) { + getSingleInstance() + .log(LogLevel.DEBUG, group, messageHash, paramsMask, messageString, args); + } + + /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */ + public static void v(IProtoLogGroup group, int messageHash, int paramsMask, + @Nullable String messageString, + Object... args) { + getSingleInstance().log(LogLevel.VERBOSE, group, messageHash, paramsMask, messageString, + args); + } + + /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */ + public static void i(IProtoLogGroup group, int messageHash, int paramsMask, + @Nullable String messageString, + Object... args) { + getSingleInstance().log(LogLevel.INFO, group, messageHash, paramsMask, messageString, args); + } + + /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */ + public static void w(IProtoLogGroup group, int messageHash, int paramsMask, + @Nullable String messageString, + Object... args) { + getSingleInstance().log(LogLevel.WARN, group, messageHash, paramsMask, messageString, args); + } + + /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */ + public static void e(IProtoLogGroup group, int messageHash, int paramsMask, + @Nullable String messageString, + Object... args) { + getSingleInstance() + .log(LogLevel.ERROR, group, messageHash, paramsMask, messageString, args); + } + + /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */ + public static void wtf(IProtoLogGroup group, int messageHash, int paramsMask, + @Nullable String messageString, + Object... args) { + getSingleInstance().log(LogLevel.WTF, group, messageHash, paramsMask, messageString, args); + } + + /** Returns true iff logging is enabled for the given {@code IProtoLogGroup}. */ + public static boolean isEnabled(IProtoLogGroup group) { + return group.isLogToLogcat() + || (group.isLogToProto() && getSingleInstance().isProtoEnabled()); + } + + /** + * Returns the single instance of the ProtoLogImpl singleton class. + */ + public static synchronized ShellProtoLogImpl getSingleInstance() { + if (sServiceInstance == null) { + sServiceInstance = new ShellProtoLogImpl(); + } + return sServiceInstance; + } + + public void startTextLogging(Context context, String... groups) { + try { + mViewerConfig.loadViewerConfig( + context.getResources().openRawResource(R.raw.wm_shell_protolog)); + setLogging(true /* setTextLogging */, true, mSystemOutWriter, groups); + } catch (IOException e) { + Log.i(TAG, "Unable to load log definitions: IOException while reading " + + "wm_shell_protolog. " + e); + } catch (JSONException e) { + Log.i(TAG, "Unable to load log definitions: JSON parsing exception while reading " + + "wm_shell_protolog. " + e); + } + } + + public void stopTextLogging(String... groups) { + setLogging(true /* setTextLogging */, false, mSystemOutWriter, groups); + } + + private ShellProtoLogImpl() { + super(new File(LOG_FILENAME), null, BUFFER_CAPACITY, + new ProtoLogViewerConfigReader()); + mSystemOutWriter = new PrintWriter(System.out, true); + } +} + diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp index 80b555be97dd..45da008c3e8e 100644 --- a/libs/input/MouseCursorController.cpp +++ b/libs/input/MouseCursorController.cpp @@ -168,7 +168,7 @@ void MouseCursorController::fade(PointerControllerInterface::Transition transiti updatePointerLocked(); } else { mLocked.pointerFadeDirection = -1; - mContext.startAnimation(); + startAnimationLocked(); } } @@ -185,7 +185,7 @@ void MouseCursorController::unfade(PointerControllerInterface::Transition transi updatePointerLocked(); } else { mLocked.pointerFadeDirection = 1; - mContext.startAnimation(); + startAnimationLocked(); } } @@ -312,10 +312,9 @@ void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) { updatePointerLocked(); } -bool MouseCursorController::doFadingAnimation(nsecs_t timestamp, bool keepAnimating) { +bool MouseCursorController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) { nsecs_t frameDelay = timestamp - mContext.getAnimationTime(); - - std::scoped_lock lock(mLock); + bool keepAnimating = false; // Animate pointer fade. if (mLocked.pointerFadeDirection < 0) { @@ -337,13 +336,10 @@ bool MouseCursorController::doFadingAnimation(nsecs_t timestamp, bool keepAnimat } updatePointerLocked(); } - return keepAnimating; } -bool MouseCursorController::doBitmapAnimation(nsecs_t timestamp) { - std::scoped_lock lock(mLock); - +bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) { std::map<int32_t, PointerAnimation>::const_iterator iter = mLocked.animationResources.find(mLocked.requestedPointerType); if (iter == mLocked.animationResources.end()) { @@ -364,7 +360,6 @@ bool MouseCursorController::doBitmapAnimation(nsecs_t timestamp) { spriteController->closeTransaction(); } - // Keep animating. return true; } @@ -399,7 +394,7 @@ void MouseCursorController::updatePointerLocked() REQUIRES(mLock) { if (anim_iter != mLocked.animationResources.end()) { mLocked.animationFrameIndex = 0; mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC); - mContext.startAnimation(); + startAnimationLocked(); } mLocked.pointerSprite->setIcon(iter->second); } else { @@ -457,4 +452,38 @@ bool MouseCursorController::resourcesLoaded() { return mLocked.resourcesLoaded; } +bool MouseCursorController::doAnimations(nsecs_t timestamp) { + std::scoped_lock lock(mLock); + bool keepFading = doFadingAnimationLocked(timestamp); + bool keepBitmap = doBitmapAnimationLocked(timestamp); + bool keepAnimating = keepFading || keepBitmap; + if (!keepAnimating) { + /* + * We know that this callback will be removed before another + * is added. mLock in PointerAnimator will not be released + * until after this is removed, and adding another callback + * requires that lock. Thus it's safe to set mLocked.animating + * here. + */ + mLocked.animating = false; + } + return keepAnimating; +} + +void MouseCursorController::startAnimationLocked() REQUIRES(mLock) { + using namespace std::placeholders; + + if (mLocked.animating) { + return; + } + mLocked.animating = true; + + std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1); + /* + * Using -1 for displayId here to avoid removing the callback + * if a TouchSpotController with the same display is removed. + */ + mContext.addAnimationCallback(-1, func); +} + } // namespace android diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h index 448165b5ac46..e6dfc4c6f99a 100644 --- a/libs/input/MouseCursorController.h +++ b/libs/input/MouseCursorController.h @@ -25,6 +25,7 @@ #include <utils/Looper.h> #include <utils/RefBase.h> +#include <functional> #include <map> #include <memory> #include <vector> @@ -61,8 +62,7 @@ public: void getAdditionalMouseResources(); bool isViewportValid(); - bool doBitmapAnimation(nsecs_t timestamp); - bool doFadingAnimation(nsecs_t timestamp, bool keepAnimating); + bool doAnimations(nsecs_t timestamp); bool resourcesLoaded(); @@ -96,6 +96,8 @@ private: int32_t buttonState; + bool animating{false}; + } mLocked GUARDED_BY(mLock); bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; @@ -104,6 +106,11 @@ private: void updatePointerLocked(); void loadResourcesLocked(bool getAdditionalMouseResources); + + bool doBitmapAnimationLocked(nsecs_t timestamp); + bool doFadingAnimationLocked(nsecs_t timestamp); + + void startAnimationLocked(); }; } // namespace android diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 14c96cefd462..8f04cfb70469 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -57,7 +57,6 @@ std::shared_ptr<PointerController> PointerController::create( controller->mContext.setHandlerController(controller); controller->mContext.setCallbackController(controller); - controller->mContext.initializeDisplayEventReceiver(); return controller; } @@ -189,24 +188,6 @@ void PointerController::setCustomPointerIcon(const SpriteIcon& icon) { mCursorController.setCustomPointerIcon(icon); } -void PointerController::doAnimate(nsecs_t timestamp) { - std::scoped_lock lock(mLock); - - mContext.setAnimationPending(false); - - bool keepFading = false; - keepFading = mCursorController.doFadingAnimation(timestamp, keepFading); - - for (auto& [displayID, spotController] : mLocked.spotControllers) { - keepFading = spotController.doFadingAnimation(timestamp, keepFading); - } - - bool keepBitmapFlipping = mCursorController.doBitmapAnimation(timestamp); - if (keepFading || keepBitmapFlipping) { - mContext.startAnimation(); - } -} - void PointerController::doInactivityTimeout() { fade(Transition::GRADUAL); } @@ -221,6 +202,11 @@ void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& for (auto it = mLocked.spotControllers.begin(); it != mLocked.spotControllers.end();) { int32_t displayID = it->first; if (!displayIdSet.count(displayID)) { + /* + * Ensures that an in-progress animation won't dereference + * a null pointer to TouchSpotController. + */ + mContext.removeAnimationCallback(displayID); it = mLocked.spotControllers.erase(it); } else { ++it; diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index 1f561da333b1..827fcf1e1bc1 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -70,7 +70,6 @@ public: void setCustomPointerIcon(const SpriteIcon& icon); void setInactivityTimeout(InactivityTimeout inactivityTimeout); void doInactivityTimeout(); - void doAnimate(nsecs_t timestamp); void reloadPointerResources(); void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports); diff --git a/libs/input/PointerControllerContext.cpp b/libs/input/PointerControllerContext.cpp index 2d7e22b01112..f30e8d8e33a5 100644 --- a/libs/input/PointerControllerContext.cpp +++ b/libs/input/PointerControllerContext.cpp @@ -38,10 +38,10 @@ PointerControllerContext::PointerControllerContext( mSpriteController(spriteController), mHandler(new MessageHandler()), mCallback(new LooperCallback()), - mController(controller) { + mController(controller), + mAnimator(*this) { std::scoped_lock lock(mLock); mLocked.inactivityTimeout = InactivityTimeout::NORMAL; - mLocked.animationPending = false; } PointerControllerContext::~PointerControllerContext() { @@ -57,15 +57,6 @@ void PointerControllerContext::setInactivityTimeout(InactivityTimeout inactivity } } -void PointerControllerContext::startAnimation() { - std::scoped_lock lock(mLock); - if (!mLocked.animationPending) { - mLocked.animationPending = true; - mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC); - mDisplayEventReceiver.requestNextVsync(); - } -} - void PointerControllerContext::resetInactivityTimeout() { std::scoped_lock lock(mLock); resetInactivityTimeoutLocked(); @@ -85,14 +76,8 @@ void PointerControllerContext::removeInactivityTimeout() { mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT); } -void PointerControllerContext::setAnimationPending(bool animationPending) { - std::scoped_lock lock(mLock); - mLocked.animationPending = animationPending; -} - -nsecs_t PointerControllerContext::getAnimationTime() { - std::scoped_lock lock(mLock); - return mLocked.animationTime; +nsecs_t PointerControllerContext::getAnimationTime() REQUIRES(mAnimator.mLock) { + return mAnimator.getAnimationTimeLocked(); } void PointerControllerContext::setHandlerController(std::shared_ptr<PointerController> controller) { @@ -112,31 +97,8 @@ sp<SpriteController> PointerControllerContext::getSpriteController() { return mSpriteController; } -void PointerControllerContext::initializeDisplayEventReceiver() { - if (mDisplayEventReceiver.initCheck() == NO_ERROR) { - mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, Looper::EVENT_INPUT, - mCallback, nullptr); - } else { - ALOGE("Failed to initialize DisplayEventReceiver."); - } -} - void PointerControllerContext::handleDisplayEvents() { - bool gotVsync = false; - ssize_t n; - nsecs_t timestamp; - DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { - for (size_t i = 0; i < static_cast<size_t>(n); ++i) { - if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { - timestamp = buf[i].header.timestamp; - gotVsync = true; - } - } - } - if (gotVsync) { - mController.doAnimate(timestamp); - } + mAnimator.handleVsyncEvents(); } void PointerControllerContext::MessageHandler::handleMessage(const Message& message) { @@ -176,4 +138,91 @@ int PointerControllerContext::LooperCallback::handleEvent(int /* fd */, int even return 1; // keep the callback } +void PointerControllerContext::addAnimationCallback(int32_t displayId, + std::function<bool(nsecs_t)> callback) { + mAnimator.addCallback(displayId, callback); +} + +void PointerControllerContext::removeAnimationCallback(int32_t displayId) { + mAnimator.removeCallback(displayId); +} + +PointerControllerContext::PointerAnimator::PointerAnimator(PointerControllerContext& context) + : mContext(context) { + initializeDisplayEventReceiver(); +} + +void PointerControllerContext::PointerAnimator::initializeDisplayEventReceiver() { + if (mDisplayEventReceiver.initCheck() == NO_ERROR) { + mContext.mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, + Looper::EVENT_INPUT, mContext.mCallback, nullptr); + } else { + ALOGE("Failed to initialize DisplayEventReceiver."); + } +} + +void PointerControllerContext::PointerAnimator::addCallback(int32_t displayId, + std::function<bool(nsecs_t)> callback) { + std::scoped_lock lock(mLock); + mLocked.callbacks[displayId] = callback; + startAnimationLocked(); +} + +void PointerControllerContext::PointerAnimator::removeCallback(int32_t displayId) { + std::scoped_lock lock(mLock); + auto it = mLocked.callbacks.find(displayId); + if (it == mLocked.callbacks.end()) { + return; + } + mLocked.callbacks.erase(it); +} + +void PointerControllerContext::PointerAnimator::handleVsyncEvents() { + bool gotVsync = false; + ssize_t n; + nsecs_t timestamp; + DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; + while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { + for (size_t i = 0; i < static_cast<size_t>(n); ++i) { + if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { + timestamp = buf[i].header.timestamp; + gotVsync = true; + } + } + } + if (gotVsync) { + std::scoped_lock lock(mLock); + mLocked.animationPending = false; + handleCallbacksLocked(timestamp); + } +} + +nsecs_t PointerControllerContext::PointerAnimator::getAnimationTimeLocked() REQUIRES(mLock) { + return mLocked.animationTime; +} + +void PointerControllerContext::PointerAnimator::startAnimationLocked() REQUIRES(mLock) { + if (!mLocked.animationPending) { + mLocked.animationPending = true; + mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC); + mDisplayEventReceiver.requestNextVsync(); + } +} + +void PointerControllerContext::PointerAnimator::handleCallbacksLocked(nsecs_t timestamp) + REQUIRES(mLock) { + for (auto it = mLocked.callbacks.begin(); it != mLocked.callbacks.end();) { + bool keepCallback = it->second(timestamp); + if (!keepCallback) { + it = mLocked.callbacks.erase(it); + } else { + ++it; + } + } + + if (!mLocked.callbacks.empty()) { + startAnimationLocked(); + } +} + } // namespace android diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h index 92e1bda25f56..98073fea323e 100644 --- a/libs/input/PointerControllerContext.h +++ b/libs/input/PointerControllerContext.h @@ -26,6 +26,7 @@ #include <utils/Looper.h> #include <utils/RefBase.h> +#include <functional> #include <map> #include <memory> #include <vector> @@ -35,6 +36,8 @@ namespace android { class PointerController; +class MouseCursorController; +class TouchSpotController; /* * Pointer resources. @@ -96,7 +99,6 @@ public: void startAnimation(); void setInactivityTimeout(InactivityTimeout inactivityTimeout); - void setAnimationPending(bool animationPending); nsecs_t getAnimationTime(); void clearSpotsByDisplay(int32_t displayId); @@ -107,9 +109,11 @@ public: sp<PointerControllerPolicyInterface> getPolicy(); sp<SpriteController> getSpriteController(); - void initializeDisplayEventReceiver(); void handleDisplayEvents(); + void addAnimationCallback(int32_t displayId, std::function<bool(nsecs_t)> callback); + void removeAnimationCallback(int32_t displayId); + class MessageHandler : public virtual android::MessageHandler { public: enum { @@ -127,22 +131,47 @@ public: }; private: + class PointerAnimator { + public: + PointerAnimator(PointerControllerContext& context); + + void addCallback(int32_t displayId, std::function<bool(nsecs_t)> callback); + void removeCallback(int32_t displayId); + void handleVsyncEvents(); + nsecs_t getAnimationTimeLocked(); + + mutable std::mutex mLock; + + private: + struct Locked { + bool animationPending{false}; + nsecs_t animationTime{systemTime(SYSTEM_TIME_MONOTONIC)}; + + std::unordered_map<int32_t, std::function<bool(nsecs_t)>> callbacks; + } mLocked GUARDED_BY(mLock); + + DisplayEventReceiver mDisplayEventReceiver; + + PointerControllerContext& mContext; + + void initializeDisplayEventReceiver(); + void startAnimationLocked(); + void handleCallbacksLocked(nsecs_t timestamp); + }; + sp<PointerControllerPolicyInterface> mPolicy; sp<Looper> mLooper; sp<SpriteController> mSpriteController; sp<MessageHandler> mHandler; sp<LooperCallback> mCallback; - DisplayEventReceiver mDisplayEventReceiver; - PointerController& mController; + PointerAnimator mAnimator; + mutable std::mutex mLock; struct Locked { - bool animationPending; - nsecs_t animationTime; - InactivityTimeout inactivityTimeout; } mLocked GUARDED_BY(mLock); diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp index c7430ceead41..f7c685ff8ba6 100644 --- a/libs/input/TouchSpotController.cpp +++ b/libs/input/TouchSpotController.cpp @@ -142,7 +142,8 @@ TouchSpotController::Spot* TouchSpotController::getSpot(uint32_t id, } TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t id, - std::vector<Spot*>& spots) { + std::vector<Spot*>& spots) + REQUIRES(mLock) { // Remove spots until we have fewer than MAX_SPOTS remaining. while (spots.size() >= MAX_SPOTS) { Spot* spot = removeFirstFadingSpotLocked(spots); @@ -186,14 +187,13 @@ void TouchSpotController::releaseSpotLocked(Spot* spot) REQUIRES(mLock) { if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) { mLocked.recycledSprites.push_back(spot->sprite); } - delete spot; } void TouchSpotController::fadeOutAndReleaseSpotLocked(Spot* spot) REQUIRES(mLock) { if (spot->id != Spot::INVALID_ID) { spot->id = Spot::INVALID_ID; - mContext.startAnimation(); + startAnimationLocked(); } } @@ -209,8 +209,24 @@ void TouchSpotController::reloadSpotResources() { mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId); } -bool TouchSpotController::doFadingAnimation(nsecs_t timestamp, bool keepAnimating) { +bool TouchSpotController::doAnimations(nsecs_t timestamp) { std::scoped_lock lock(mLock); + bool keepAnimating = doFadingAnimationLocked(timestamp); + if (!keepAnimating) { + /* + * We know that this callback will be removed before another + * is added. mLock in PointerAnimator will not be released + * until after this is removed, and adding another callback + * requires that lock. Thus it's safe to set mLocked.animating + * here. + */ + mLocked.animating = false; + } + return keepAnimating; +} + +bool TouchSpotController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) { + bool keepAnimating = false; nsecs_t animationTime = mContext.getAnimationTime(); nsecs_t frameDelay = timestamp - animationTime; size_t numSpots = mLocked.displaySpots.size(); @@ -233,4 +249,16 @@ bool TouchSpotController::doFadingAnimation(nsecs_t timestamp, bool keepAnimatin return keepAnimating; } +void TouchSpotController::startAnimationLocked() REQUIRES(mLock) { + using namespace std::placeholders; + + if (mLocked.animating) { + return; + } + mLocked.animating = true; + + std::function<bool(nsecs_t)> func = std::bind(&TouchSpotController::doAnimations, this, _1); + mContext.addAnimationCallback(mDisplayId, func); +} + } // namespace android diff --git a/libs/input/TouchSpotController.h b/libs/input/TouchSpotController.h index f3b355010bee..703de3603f48 100644 --- a/libs/input/TouchSpotController.h +++ b/libs/input/TouchSpotController.h @@ -17,6 +17,8 @@ #ifndef _UI_TOUCH_SPOT_CONTROLLER_H #define _UI_TOUCH_SPOT_CONTROLLER_H +#include <functional> + #include "PointerControllerContext.h" namespace android { @@ -34,7 +36,7 @@ public: void clearSpots(); void reloadSpotResources(); - bool doFadingAnimation(nsecs_t timestamp, bool keepAnimating); + bool doAnimations(nsecs_t timestamp); private: struct Spot { @@ -76,6 +78,8 @@ private: std::vector<Spot*> displaySpots; std::vector<sp<Sprite>> recycledSprites; + bool animating{false}; + } mLocked GUARDED_BY(mLock); Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots); @@ -84,6 +88,8 @@ private: void releaseSpotLocked(Spot* spot); void fadeOutAndReleaseSpotLocked(Spot* spot); void fadeOutAndReleaseAllSpotsLocked(); + bool doFadingAnimationLocked(nsecs_t timestamp); + void startAnimationLocked(); }; } // namespace android diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 1803027743f6..6fc702e4a068 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -29,6 +29,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; @@ -215,6 +217,7 @@ public class LocationManager { * @see #EXTRA_PROVIDER_ENABLED * @see #isProviderEnabled(String) */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED"; /** @@ -243,6 +246,7 @@ public class LocationManager { * @see #EXTRA_LOCATION_ENABLED * @see #isLocationEnabled() */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED"; /** diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index e0ebec6cbd01..e1b3151acd5b 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -11617,6 +11617,16 @@ package android.content.pm { field public int version; } + public final class FileChecksum implements android.os.Parcelable { + method public int describeContents(); + method public int getKind(); + method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException; + method @Nullable public String getSplitName(); + method @NonNull public byte[] getValue(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.FileChecksum> CREATOR; + } + public final class InstallSourceInfo implements android.os.Parcelable { method public int describeContents(); method @Nullable public String getInitiatingPackageName(); @@ -11992,6 +12002,7 @@ package android.content.pm { method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public CharSequence getBackgroundPermissionOptionLabel(); method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int); + method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException; method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName); method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon(); method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo); @@ -12082,6 +12093,7 @@ package android.content.pm { field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3 field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1 field public static final int DONT_KILL_APP = 1; // 0x1 + field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS"; field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID"; field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT"; field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays"; @@ -12236,6 +12248,8 @@ package android.content.pm { field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000 field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000 field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L + field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20 + field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40 field public static final int PERMISSION_DENIED = -1; // 0xffffffff field public static final int PERMISSION_GRANTED = 0; // 0x0 field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff @@ -12245,9 +12259,16 @@ package android.content.pm { field public static final int SIGNATURE_SECOND_NOT_SIGNED = -2; // 0xfffffffe field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc field public static final int SYNCHRONOUS = 2; // 0x2 + field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL; + field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE; field public static final int VERIFICATION_ALLOW = 1; // 0x1 field public static final int VERIFICATION_REJECT = -1; // 0xffffffff field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff + field public static final int WHOLE_MD5 = 2; // 0x2 + field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1 + field public static final int WHOLE_SHA1 = 4; // 0x4 + field public static final int WHOLE_SHA256 = 8; // 0x8 + field public static final int WHOLE_SHA512 = 16; // 0x10 } public static class PackageManager.NameNotFoundException extends android.util.AndroidException { diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index d1264dfdd36d..7cce0f2f4b3e 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -219,6 +219,7 @@ package android { field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT"; field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE"; field public static final String SHUTDOWN = "android.permission.SHUTDOWN"; + field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE"; field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES"; field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"; field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON"; diff --git a/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java index 552cadfe967e..b17ad0febb90 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java @@ -16,7 +16,9 @@ package com.android.systemui; +import com.android.systemui.dagger.GlobalModule; import com.android.systemui.dagger.GlobalRootComponent; +import com.android.systemui.dagger.WMModule; import javax.inject.Singleton; @@ -26,7 +28,9 @@ import dagger.Component; @Singleton @Component( modules = { - CarSysUIComponentModule.class + GlobalModule.class, + CarSysUIComponentModule.class, + WMModule.class }) public interface CarGlobalRootComponent extends GlobalRootComponent { /** diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java index 24d9d09d2ca9..1a6fdfa9c996 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java @@ -16,11 +16,9 @@ package com.android.systemui; -import com.android.systemui.dagger.DependencyBinder; import com.android.systemui.dagger.DependencyProvider; import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.SystemServicesModule; import com.android.systemui.dagger.SystemUIModule; import com.android.systemui.pip.phone.dagger.PipModule; @@ -33,9 +31,7 @@ import dagger.Subcomponent; @Subcomponent(modules = { CarComponentBinder.class, DependencyProvider.class, - DependencyBinder.class, PipModule.class, - SystemServicesModule.class, SystemUIModule.class, CarSystemUIModule.class, CarSystemUIBinder.class}) diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java index 3b22fdb50765..38e1a48ab3a7 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java @@ -299,10 +299,10 @@ public class NotificationPanelViewController extends OverlayPanelViewController // The glass pane is used to view touch events before passed to the notification list. // This allows us to initialize gesture listeners and detect when to close the notifications glassPane.setOnTouchListener((v, event) -> { - if (event.getActionMasked() == MotionEvent.ACTION_UP) { + if (isClosingAction(event)) { mNotificationListAtEndAtTimeOfTouch = false; } - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + if (isOpeningAction(event)) { mFirstTouchDownOnGlassPane = event.getRawX(); mNotificationListAtEndAtTimeOfTouch = mNotificationListAtEnd; // Reset the tracker when there is a touch down on the glass pane. @@ -355,8 +355,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController if (rect != null) { clippedHeight = rect.bottom; } - if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP - && mIsSwipingVerticallyToClose) { + if (!handled && isClosingAction(event) && mIsSwipingVerticallyToClose) { if (getSettleClosePercentage() < getPercentageFromEndingEdge() && isTracking) { animatePanel(DEFAULT_FLING_VELOCITY, false); } else if (clippedHeight != getLayout().getHeight() && isTracking) { @@ -369,7 +368,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController // Updating the mNotificationListAtEndAtTimeOfTouch state has to be done after // the event has been passed to the closeGestureDetector above, such that the // closeGestureDetector sees the up event before the state has changed. - if (event.getActionMasked() == MotionEvent.ACTION_UP) { + if (isClosingAction(event)) { mNotificationListAtEndAtTimeOfTouch = false; } return handled || isTracking; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java index 45808a8a0b3e..bde31f18d8fd 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java @@ -191,6 +191,38 @@ public abstract class OverlayPanelViewController extends OverlayViewController { } } + /** Checks if a {@link MotionEvent} is an action to open the panel. + * @param e {@link MotionEvent} to check. + * @return true only if opening action. + */ + protected boolean isOpeningAction(MotionEvent e) { + if (mAnimateDirection == POSITIVE_DIRECTION) { + return e.getActionMasked() == MotionEvent.ACTION_DOWN; + } + + if (mAnimateDirection == NEGATIVE_DIRECTION) { + return e.getActionMasked() == MotionEvent.ACTION_UP; + } + + return false; + } + + /** Checks if a {@link MotionEvent} is an action to close the panel. + * @param e {@link MotionEvent} to check. + * @return true only if closing action. + */ + protected boolean isClosingAction(MotionEvent e) { + if (mAnimateDirection == POSITIVE_DIRECTION) { + return e.getActionMasked() == MotionEvent.ACTION_UP; + } + + if (mAnimateDirection == NEGATIVE_DIRECTION) { + return e.getActionMasked() == MotionEvent.ACTION_DOWN; + } + + return false; + } + /* ***************************************************************************************** * * Panel Animation * ***************************************************************************************** */ @@ -243,8 +275,7 @@ public abstract class OverlayPanelViewController extends OverlayViewController { * Depending on certain conditions, determines whether to fully expand or collapse the panel. */ protected void maybeCompleteAnimation(MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_UP - && isPanelVisible()) { + if (isClosingAction(event) && isPanelVisible()) { if (mSettleClosePercentage < mPercentageFromEndingEdge) { animatePanel(DEFAULT_FLING_VELOCITY, false); } else { diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java index fd6685ffdb8f..6d31a8d69ebe 100644 --- a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java @@ -22,8 +22,6 @@ import android.view.IWindowManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.pip.phone.PipMenuActivity; -import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; import com.android.systemui.wm.DisplaySystemBarsController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; @@ -43,12 +41,4 @@ public class CarWMShellModule { return new DisplaySystemBarsController(context, wmService, displayController, mainHandler, transactionPool); } - - /** TODO(b/150319024): PipMenuActivity will move to a Window */ - @SysUISingleton - @PipMenuActivityClass - @Provides - Class<?> providePipMenuActivityClass() { - return PipMenuActivity.class; - } } diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index f42bf1982b36..11d1b0a9ef2a 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -483,6 +483,13 @@ public class ExternalStorageProvider extends FileSystemProvider { } @Override + protected void onDocIdDeleted(String docId) { + Uri uri = DocumentsContract.buildDocumentUri(AUTHORITY, docId); + getContext().revokeUriPermission(uri, ~0); + } + + + @Override public Cursor queryRoots(String[] projection) throws FileNotFoundException { final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection)); synchronized (mRootsLock) { diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_0.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_0.xml new file mode 100644 index 000000000000..16e91903084f --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_0.xml @@ -0,0 +1,31 @@ +<!-- + 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="25.50" + android:viewportHeight="25.50"> + <group + android:translateX="0.77" + android:translateY="0.23" > + <path + android:pathData="M14,12h6.54l3.12,-3.89c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3C6.44,3 2.33,5.36 0.56,6.57C0.05,6.92 -0.05,7.63 0.33,8.11L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L14,20.13V12z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M22.71,15.67l-1.83,1.83l1.83,1.83c0.38,0.38 0.38,1 0,1.38v0c-0.38,0.38 -1,0.39 -1.38,0l-1.83,-1.83l-1.83,1.83c-0.38,0.38 -1,0.38 -1.38,0l-0.01,-0.01c-0.38,-0.38 -0.38,-1 0,-1.38l1.83,-1.83l-1.82,-1.82c-0.38,-0.38 -0.38,-1 0,-1.38l0.01,-0.01c0.38,-0.38 1,-0.38 1.38,0l1.82,1.82l1.82,-1.82c0.38,-0.38 1,-0.38 1.38,0l0,0C23.09,14.67 23.09,15.29 22.71,15.67z" + android:fillColor="#FFFFFF"/> + </group> +</vector> diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_1.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_1.xml new file mode 100644 index 000000000000..4c338c968194 --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_1.xml @@ -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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="@android:color/white" + android:pathData="M22,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.41z"/> + <path + android:fillColor="@android:color/white" + android:pathData="M12,2.01C7.25,2.01 2.97,4.09 0,7.4L7.582,16.625C7.582,16.627 7.58,16.629 7.58,16.631L11.99,22L12,22L13,20.789L13,17.641L13,13.119C12.68,13.039 12.34,13 12,13C10.601,13 9.351,13.64 8.531,14.639L2.699,7.539C5.269,5.279 8.58,4.01 12,4.01C15.42,4.01 18.731,5.279 21.301,7.539L16.811,13L19.4,13L24,7.4C21.03,4.09 16.75,2.01 12,2.01z"/> +</vector> diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_2.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_2.xml new file mode 100644 index 000000000000..79037dbccf2d --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_2.xml @@ -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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="@android:color/white" + android:pathData="M22,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.41z"/> + <path + android:fillColor="@android:color/white" + android:pathData="M12,2C7.25,2 2.97,4.081 0,7.391L12,22L13,20.779L13,17.631L13,13L16.801,13L18,13L19.391,13L24,7.391C21.03,4.081 16.75,2 12,2zM12,4C14.747,4 17.423,4.819 19.701,6.313C20.259,6.678 20.795,7.085 21.301,7.529L17.389,12.287C16.029,10.868 14.119,9.99 12,9.99C9.88,9.99 7.969,10.869 6.609,12.289L2.699,7.529C5.269,5.269 8.58,4 12,4z"/> +</vector> diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_3.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_3.xml new file mode 100644 index 000000000000..21ad128f81ff --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_3.xml @@ -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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="@android:color/white" + android:pathData="M22,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.41z"/> + <path + android:fillColor="@android:color/white" + android:pathData="M12,2C7.25,2 2.97,4.081 0,7.391L3.301,11.41L12,22L13,20.779L13,17.631L13,13L16.801,13L19.391,13L20.699,11.41C20.699,11.409 20.698,11.409 20.697,11.408L24,7.391C21.03,4.081 16.75,2 12,2zM12,4C15.42,4 18.731,5.269 21.301,7.529L19.35,9.9C17.43,8.1 14.86,6.99 12,6.99C9.14,6.99 6.57,8.1 4.65,9.9C4.65,9.901 4.649,9.902 4.648,9.902L2.699,7.529C5.269,5.269 8.58,4 12,4z"/> +</vector> diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_4.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_4.xml new file mode 100644 index 000000000000..2ec5ba30cdc3 --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_4.xml @@ -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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="@android:color/white" + android:pathData="M12,2C7.25,2 2.97,4.08 0,7.39L12,22l1,-1.22V13h6.39L24,7.39C21.03,4.08 16.75,2 12,2z"/> + <path + android:fillColor="@android:color/white" + android:pathData="M22,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.41z"/> +</vector> diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index a43412e116c8..b2808061586b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -49,11 +49,19 @@ public class Utils { private static String sSharedSystemSharedLibPackageName; static final int[] WIFI_PIE = { - com.android.internal.R.drawable.ic_wifi_signal_0, - com.android.internal.R.drawable.ic_wifi_signal_1, - com.android.internal.R.drawable.ic_wifi_signal_2, - com.android.internal.R.drawable.ic_wifi_signal_3, - com.android.internal.R.drawable.ic_wifi_signal_4 + com.android.internal.R.drawable.ic_wifi_signal_0, + com.android.internal.R.drawable.ic_wifi_signal_1, + com.android.internal.R.drawable.ic_wifi_signal_2, + com.android.internal.R.drawable.ic_wifi_signal_3, + com.android.internal.R.drawable.ic_wifi_signal_4 + }; + + static final int[] SHOW_X_WIFI_PIE = { + R.drawable.ic_show_x_wifi_signal_0, + R.drawable.ic_show_x_wifi_signal_1, + R.drawable.ic_show_x_wifi_signal_2, + R.drawable.ic_show_x_wifi_signal_3, + R.drawable.ic_show_x_wifi_signal_4 }; public static void updateLocationEnabled(Context context, boolean enabled, int userId, @@ -353,10 +361,22 @@ public class Utils { * @throws IllegalArgumentException if an invalid RSSI level is given. */ public static int getWifiIconResource(int level) { + return getWifiIconResource(false /* showX */, level); + } + + /** + * Returns the Wifi icon resource for a given RSSI level. + * + * @param showX True if a connected Wi-Fi network has the problem which should show Pie+x + * signal icon to users. + * @param level The number of bars to show (0-4) + * @throws IllegalArgumentException if an invalid RSSI level is given. + */ + public static int getWifiIconResource(boolean showX, int level) { if (level < 0 || level >= WIFI_PIE.length) { throw new IllegalArgumentException("No Wifi icon found for level: " + level); } - return WIFI_PIE[level]; + return showX ? SHOW_X_WIFI_PIE[level] : WIFI_PIE[level]; } public static int getDefaultStorageManagerDaysToRetain(Resources resources) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java index a53bc9f966d2..bba69f29a290 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java @@ -35,6 +35,7 @@ import androidx.preference.PreferenceViewHolder; import com.android.settingslib.R; import com.android.settingslib.Utils; import com.android.wifitrackerlib.WifiEntry; +import com.android.wifitrackerlib.WifiEntry.ConnectedInfo; /** * Preference to display a WifiEntry in a wifi picker. @@ -64,6 +65,7 @@ public class WifiEntryPreference extends Preference implements WifiEntry.WifiEnt private final IconInjector mIconInjector; private WifiEntry mWifiEntry; private int mLevel = -1; + private boolean mShowX; // Shows the Wi-Fi signl icon of Pie+x when it's true. private CharSequence mContentDescription; private OnButtonClickListener mOnButtonClickListener; @@ -136,9 +138,15 @@ public class WifiEntryPreference extends Preference implements WifiEntry.WifiEnt public void refresh() { setTitle(mWifiEntry.getTitle()); final int level = mWifiEntry.getLevel(); - if (level != mLevel) { + final ConnectedInfo connectedInfo = mWifiEntry.getConnectedInfo(); + boolean showX = false; + if (connectedInfo != null) { + showX = !connectedInfo.isDefaultNetwork || !connectedInfo.isValidated; + } + if (level != mLevel || showX != mShowX) { mLevel = level; - updateIcon(mLevel); + mShowX = showX; + updateIcon(mShowX, mLevel); notifyChanged(); } @@ -184,13 +192,13 @@ public class WifiEntryPreference extends Preference implements WifiEntry.WifiEnt } - private void updateIcon(int level) { + private void updateIcon(boolean showX, int level) { if (level == -1) { setIcon(null); return; } - final Drawable drawable = mIconInjector.getIcon(level); + final Drawable drawable = mIconInjector.getIcon(showX, level); if (drawable != null) { drawable.setTintList(Utils.getColorAttr(getContext(), android.R.attr.colorControlNormal)); @@ -260,8 +268,8 @@ public class WifiEntryPreference extends Preference implements WifiEntry.WifiEnt mContext = context; } - public Drawable getIcon(int level) { - return mContext.getDrawable(Utils.getWifiIconResource(level)); + public Drawable getIcon(boolean showX, int level) { + return mContext.getDrawable(Utils.getWifiIconResource(showX, level)); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java index 46e699d3bed5..40af7dc797b3 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java @@ -17,6 +17,7 @@ package com.android.settingslib.wifi; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.content.Context; @@ -29,6 +30,7 @@ import androidx.preference.PreferenceViewHolder; import com.android.settingslib.R; import com.android.wifitrackerlib.WifiEntry; +import com.android.wifitrackerlib.WifiEntry.ConnectedInfo; import org.junit.Before; import org.junit.Test; @@ -62,6 +64,17 @@ public class WifiEntryPreferenceTest { @Mock private Drawable mMockDrawable4; + @Mock + private Drawable mMockShowXDrawable0; + @Mock + private Drawable mMockShowXDrawable1; + @Mock + private Drawable mMockShowXDrawable2; + @Mock + private Drawable mMockShowXDrawable3; + @Mock + private Drawable mMockShowXDrawable4; + private static final String MOCK_TITLE = "title"; private static final String MOCK_SUMMARY = "summary"; private static final String FAKE_URI_STRING = "fakeuri"; @@ -75,11 +88,22 @@ public class WifiEntryPreferenceTest { when(mMockWifiEntry.getTitle()).thenReturn(MOCK_TITLE); when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(MOCK_SUMMARY); - when(mMockIconInjector.getIcon(0)).thenReturn(mMockDrawable0); - when(mMockIconInjector.getIcon(1)).thenReturn(mMockDrawable1); - when(mMockIconInjector.getIcon(2)).thenReturn(mMockDrawable2); - when(mMockIconInjector.getIcon(3)).thenReturn(mMockDrawable3); - when(mMockIconInjector.getIcon(4)).thenReturn(mMockDrawable4); + when(mMockIconInjector.getIcon(false /* showX */, 0)).thenReturn(mMockDrawable0); + when(mMockIconInjector.getIcon(false /* showX */, 1)).thenReturn(mMockDrawable1); + when(mMockIconInjector.getIcon(false /* showX */, 2)).thenReturn(mMockDrawable2); + when(mMockIconInjector.getIcon(false /* showX */, 3)).thenReturn(mMockDrawable3); + when(mMockIconInjector.getIcon(false /* showX */, 4)).thenReturn(mMockDrawable4); + + when(mMockIconInjector.getIcon(true /* showX */, 0)) + .thenReturn(mMockShowXDrawable0); + when(mMockIconInjector.getIcon(true /* showX */, 1)) + .thenReturn(mMockShowXDrawable1); + when(mMockIconInjector.getIcon(true /* showX */, 2)) + .thenReturn(mMockShowXDrawable2); + when(mMockIconInjector.getIcon(true /* showX */, 3)) + .thenReturn(mMockShowXDrawable3); + when(mMockIconInjector.getIcon(true /* showX */, 4)) + .thenReturn(mMockShowXDrawable4); } @Test @@ -155,6 +179,70 @@ public class WifiEntryPreferenceTest { } @Test + public void levelChanged_notDefaultWifiRefresh_shouldUpdateLevelIcon() { + final List<Drawable> iconList = new ArrayList<>(); + final ConnectedInfo mockConnectedInfo = mock(ConnectedInfo.class); + mockConnectedInfo.isDefaultNetwork = false; + when(mMockWifiEntry.getConnectedInfo()).thenReturn(mockConnectedInfo); + final WifiEntryPreference pref = + new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector); + + when(mMockWifiEntry.getLevel()).thenReturn(0); + pref.refresh(); + iconList.add(pref.getIcon()); + when(mMockWifiEntry.getLevel()).thenReturn(1); + pref.refresh(); + iconList.add(pref.getIcon()); + when(mMockWifiEntry.getLevel()).thenReturn(2); + pref.refresh(); + iconList.add(pref.getIcon()); + when(mMockWifiEntry.getLevel()).thenReturn(3); + pref.refresh(); + iconList.add(pref.getIcon()); + when(mMockWifiEntry.getLevel()).thenReturn(4); + pref.refresh(); + iconList.add(pref.getIcon()); + when(mMockWifiEntry.getLevel()).thenReturn(-1); + pref.refresh(); + iconList.add(pref.getIcon()); + + assertThat(iconList).containsExactly(mMockShowXDrawable0, mMockShowXDrawable1, + mMockShowXDrawable2, mMockShowXDrawable3, mMockShowXDrawable4, null); + } + + @Test + public void levelChanged_notValidatedWifiRefresh_shouldUpdateLevelIcon() { + final List<Drawable> iconList = new ArrayList<>(); + final ConnectedInfo mockConnectedInfo = mock(ConnectedInfo.class); + mockConnectedInfo.isValidated = false; + when(mMockWifiEntry.getConnectedInfo()).thenReturn(mockConnectedInfo); + final WifiEntryPreference pref = + new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector); + + when(mMockWifiEntry.getLevel()).thenReturn(0); + pref.refresh(); + iconList.add(pref.getIcon()); + when(mMockWifiEntry.getLevel()).thenReturn(1); + pref.refresh(); + iconList.add(pref.getIcon()); + when(mMockWifiEntry.getLevel()).thenReturn(2); + pref.refresh(); + iconList.add(pref.getIcon()); + when(mMockWifiEntry.getLevel()).thenReturn(3); + pref.refresh(); + iconList.add(pref.getIcon()); + when(mMockWifiEntry.getLevel()).thenReturn(4); + pref.refresh(); + iconList.add(pref.getIcon()); + when(mMockWifiEntry.getLevel()).thenReturn(-1); + pref.refresh(); + iconList.add(pref.getIcon()); + + assertThat(iconList).containsExactly(mMockShowXDrawable0, mMockShowXDrawable1, + mMockShowXDrawable2, mMockShowXDrawable3, mMockShowXDrawable4, null); + } + + @Test public void notNull_whenGetHelpUriString_shouldSetImageButtonVisible() { when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING); final WifiEntryPreference pref = diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 2fbd9ba05817..0f2e25c7025b 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -174,6 +174,9 @@ android_app { kotlincflags: ["-Xjvm-default=enable"], dxflags: ["--multi-dex"], - required: ["privapp_whitelist_com.android.systemui"], + required: [ + "privapp_whitelist_com.android.systemui", + "checked-wm_shell_protolog.json", + ], } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index af008b996172..7f4f580abf94 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -530,20 +530,6 @@ androidprv:alwaysFocusable="true" android:excludeFromRecents="true" /> - <activity - android:name=".pip.phone.PipMenuActivity" - android:permission="com.android.systemui.permission.SELF" - android:theme="@style/PipPhoneOverlayControlTheme" - android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" - android:excludeFromRecents="true" - android:exported="false" - android:resizeableActivity="true" - android:supportsPictureInPicture="true" - android:stateNotNeeded="true" - android:taskAffinity="" - android:launchMode="singleTop" - androidprv:alwaysFocusable="true" /> - <!-- started from SliceProvider --> <activity android:name=".SlicePermissionActivity" android:theme="@style/Theme.SystemUI.Dialog.Alert" diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java index 9d52098f37d5..63f8b1f5dbb8 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java @@ -30,7 +30,7 @@ import java.io.PrintWriter; */ @ProvidesInterface(version = FalsingManager.VERSION) public interface FalsingManager { - int VERSION = 4; + int VERSION = 5; void onSuccessfulUnlock(); @@ -42,7 +42,8 @@ public interface FalsingManager { boolean isUnlockingDisabled(); - boolean isFalseTouch(); + /** Returns true if the gesture should be rejected. */ + boolean isFalseTouch(int interactionType); void onNotificatonStopDraggingDown(); diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java index 02c4c5eff26e..4b6efa91a7c8 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java @@ -14,16 +14,16 @@ package com.android.systemui.plugins.statusbar; -import com.android.systemui.plugins.annotations.DependsOn; -import com.android.systemui.plugins.annotations.ProvidesInterface; -import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; - import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; +import com.android.systemui.plugins.annotations.DependsOn; +import com.android.systemui.plugins.annotations.ProvidesInterface; +import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; + @ProvidesInterface(version = NotificationSwipeActionHelper.VERSION) @DependsOn(target = SnoozeOption.class) public interface NotificationSwipeActionHelper { @@ -52,7 +52,8 @@ public interface NotificationSwipeActionHelper { public boolean isDismissGesture(MotionEvent ev); - public boolean isFalseGesture(MotionEvent ev); + /** Returns true if the gesture should be rejected. */ + boolean isFalseGesture(); public boolean swipedFarEnough(float translation, float viewSize); diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index df66bf5a1051..6c06b0a19844 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -41,7 +41,12 @@ public <init>(android.content.Context); } +# Keep the wm shell lib -keep class com.android.wm.shell.* +# Keep the protolog group methods that are called by the generated code +-keepclassmembers class com.android.wm.shell.protolog.ShellProtoLogGroup { + *; +} -keep class com.android.systemui.dagger.GlobalRootComponent { *; } -keep class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { *; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index ecf1c2c91770..5ad8cad8195a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -22,6 +22,7 @@ import android.widget.RelativeLayout; import android.widget.TextClock; import com.android.internal.colorextraction.ColorExtractor; +import com.android.keyguard.dagger.KeyguardStatusViewScope; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.plugins.ClockPlugin; @@ -36,6 +37,7 @@ import java.util.TimeZone; /** * Switch to show plugin clock when plugin is connected, otherwise it will show default clock. */ +@KeyguardStatusViewScope public class KeyguardClockSwitch extends RelativeLayout { private static final String TAG = "KeyguardClockSwitch"; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index f17f1ca797e0..fe5fcc6fd632 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -34,10 +34,11 @@ import javax.inject.Inject; public class KeyguardClockSwitchController { private static final boolean CUSTOM_CLOCKS_ENABLED = true; + private final KeyguardClockSwitch mView; private final StatusBarStateController mStatusBarStateController; private final SysuiColorExtractor mColorExtractor; private final ClockManager mClockManager; - private KeyguardClockSwitch mView; + private final KeyguardSliceViewController mKeyguardSliceViewController; private final StatusBarStateController.StateListener mStateListener = new StatusBarStateController.StateListener() { @@ -52,9 +53,13 @@ public class KeyguardClockSwitchController { * * The color palette changes when the wallpaper is changed. */ - private final ColorExtractor.OnColorsChangedListener mColorsListener = (extractor, which) -> { - if ((which & WallpaperManager.FLAG_LOCK) != 0) { - mView.updateColors(getGradientColors()); + private final ColorExtractor.OnColorsChangedListener mColorsListener = + new ColorExtractor.OnColorsChangedListener() { + @Override + public void onColorsChanged(ColorExtractor extractor, int which) { + if ((which & WallpaperManager.FLAG_LOCK) != 0) { + mView.updateColors(getGradientColors()); + } } }; @@ -84,22 +89,27 @@ public class KeyguardClockSwitchController { }; @Inject - public KeyguardClockSwitchController(StatusBarStateController statusBarStateController, - SysuiColorExtractor colorExtractor, ClockManager clockManager) { + public KeyguardClockSwitchController(KeyguardClockSwitch keyguardClockSwitch, + StatusBarStateController statusBarStateController, + SysuiColorExtractor colorExtractor, ClockManager clockManager, + KeyguardSliceViewController keyguardSliceViewController) { + mView = keyguardClockSwitch; mStatusBarStateController = statusBarStateController; mColorExtractor = colorExtractor; mClockManager = clockManager; + mKeyguardSliceViewController = keyguardSliceViewController; } /** * Attach the controller to the view it relates to. */ - public void attach(KeyguardClockSwitch view) { - mView = view; + public void init() { if (mView.isAttachedToWindow()) { mOnAttachStateChangeListener.onViewAttachedToWindow(mView); } mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener); + + mKeyguardSliceViewController.init(); } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 6f19613be28f..be21d203411e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -34,12 +34,15 @@ import android.view.View; import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; +import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.util.InjectionInflationController; +import javax.inject.Inject; + public class KeyguardDisplayManager { protected static final String TAG = "KeyguardDisplayManager"; private static boolean DEBUG = KeyguardConstants.DEBUG; @@ -47,6 +50,7 @@ public class KeyguardDisplayManager { private final MediaRouter mMediaRouter; private final DisplayManager mDisplayService; private final InjectionInflationController mInjectableInflater; + private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; private final Context mContext; private boolean mShowing; @@ -86,10 +90,13 @@ public class KeyguardDisplayManager { } }; + @Inject public KeyguardDisplayManager(Context context, - InjectionInflationController injectableInflater) { + InjectionInflationController injectableInflater, + KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) { mContext = context; mInjectableInflater = injectableInflater; + mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; mMediaRouter = mContext.getSystemService(MediaRouter.class); mDisplayService = mContext.getSystemService(DisplayManager.class); mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */); @@ -124,6 +131,7 @@ public class KeyguardDisplayManager { Presentation presentation = mPresentations.get(displayId); if (presentation == null) { final Presentation newPresentation = new KeyguardPresentation(mContext, display, + mKeyguardStatusViewComponentFactory, mInjectableInflater.injectable(LayoutInflater.from(mContext))); newPresentation.setOnDismissListener(dialog -> { if (newPresentation.equals(mPresentations.get(displayId))) { @@ -241,7 +249,9 @@ public class KeyguardDisplayManager { static final class KeyguardPresentation extends Presentation { private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s + private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; private final LayoutInflater mInjectableLayoutInflater; + private KeyguardClockSwitchController mKeyguardClockSwitchController; private View mClock; private int mUsableWidth; private int mUsableHeight; @@ -259,8 +269,10 @@ public class KeyguardDisplayManager { }; KeyguardPresentation(Context context, Display display, + KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, LayoutInflater injectionLayoutInflater) { super(context, display, R.style.Theme_SystemUI_KeyguardPresentation); + mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; mInjectableLayoutInflater = injectionLayoutInflater; getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); setCancelable(false); @@ -302,6 +314,12 @@ public class KeyguardDisplayManager { // Avoid screen burn in mClock.post(mMoveTextRunnable); + + mKeyguardClockSwitchController = mKeyguardStatusViewComponentFactory + .build(findViewById(R.id.clock)) + .getKeyguardClockSwitchController(); + + mKeyguardClockSwitchController.init(); } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index f639c880c97a..a479bca56c2a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -16,12 +16,6 @@ package com.android.keyguard; -import static android.app.slice.Slice.HINT_LIST_ITEM; -import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.Display.INVALID_DISPLAY; - -import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; - import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; @@ -35,28 +29,19 @@ import android.graphics.drawable.Drawable; import android.graphics.text.LineBreaker; import android.net.Uri; import android.os.Trace; -import android.provider.Settings; import android.text.TextUtils; import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; -import android.util.Log; import android.util.TypedValue; -import android.view.Display; import android.view.View; import android.view.animation.Animation; import android.widget.LinearLayout; import android.widget.TextView; -import androidx.lifecycle.LiveData; -import androidx.lifecycle.Observer; -import androidx.slice.Slice; import androidx.slice.SliceItem; -import androidx.slice.SliceViewManager; import androidx.slice.core.SliceQuery; -import androidx.slice.widget.ListContent; import androidx.slice.widget.RowContent; import androidx.slice.widget.SliceContent; -import androidx.slice.widget.SliceLiveData; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; @@ -64,70 +49,49 @@ import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.keyguard.KeyguardSliceProvider; -import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.tuner.TunerService; import com.android.systemui.util.wakelock.KeepAwakeAnimationListener; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; - -import javax.inject.Inject; -import javax.inject.Named; +import java.util.Map; /** * View visible under the clock on the lock screen and AoD. */ -public class KeyguardSliceView extends LinearLayout implements View.OnClickListener, - Observer<Slice>, TunerService.Tunable, ConfigurationController.ConfigurationListener { +public class KeyguardSliceView extends LinearLayout { private static final String TAG = "KeyguardSliceView"; public static final int DEFAULT_ANIM_DURATION = 550; - private final HashMap<View, PendingIntent> mClickActions; - private final ActivityStarter mActivityStarter; - private final ConfigurationController mConfigurationController; private final LayoutTransition mLayoutTransition; - private final TunerService mTunerService; - private Uri mKeyguardSliceUri; @VisibleForTesting TextView mTitle; private Row mRow; private int mTextColor; private float mDarkAmount = 0; - private LiveData<Slice> mLiveData; - private int mDisplayId = INVALID_DISPLAY; private int mIconSize; private int mIconSizeWithHeader; /** * Runnable called whenever the view contents change. */ private Runnable mContentChangeListener; - private Slice mSlice; private boolean mHasHeader; private final int mRowWithHeaderPadding; private final int mRowPadding; private float mRowTextSize; private float mRowWithHeaderTextSize; + private View.OnClickListener mOnClickListener; - @Inject - public KeyguardSliceView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - ActivityStarter activityStarter, ConfigurationController configurationController, - TunerService tunerService, @Main Resources resources) { + public KeyguardSliceView(Context context, AttributeSet attrs) { super(context, attrs); - mTunerService = tunerService; - mClickActions = new HashMap<>(); + Resources resources = context.getResources(); mRowPadding = resources.getDimensionPixelSize(R.dimen.subtitle_clock_padding); mRowWithHeaderPadding = resources.getDimensionPixelSize(R.dimen.header_subtitle_padding); - mActivityStarter = activityStarter; - mConfigurationController = configurationController; mLayoutTransition = new LayoutTransition(); mLayoutTransition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2); @@ -153,39 +117,10 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe R.dimen.widget_label_font_size); mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize( R.dimen.header_row_font_size); - mTitle.setOnClickListener(this); mTitle.setBreakStrategy(LineBreaker.BREAK_STRATEGY_BALANCED); } @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - Display display = getDisplay(); - if (display != null) { - mDisplayId = display.getDisplayId(); - } - mTunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI); - // Make sure we always have the most current slice - if (mDisplayId == DEFAULT_DISPLAY) { - mLiveData.observeForever(this); - } - mConfigurationController.addCallback(this); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - // TODO(b/117344873) Remove below work around after this issue be fixed. - if (mDisplayId == DEFAULT_DISPLAY) { - mLiveData.removeObserver(this); - } - mTunerService.removeTunable(this); - mConfigurationController.removeCallback(this); - } - - @Override public void onVisibilityAggregated(boolean isVisible) { super.onVisibilityAggregated(isVisible); setLayoutTransition(isVisible ? mLayoutTransition : null); @@ -198,44 +133,31 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe return mHasHeader; } - private void showSlice() { - Trace.beginSection("KeyguardSliceView#showSlice"); - if (mSlice == null) { - mTitle.setVisibility(GONE); - mRow.setVisibility(GONE); - mHasHeader = false; - if (mContentChangeListener != null) { - mContentChangeListener.run(); - } - Trace.endSection(); - return; - } - mClickActions.clear(); - - ListContent lc = new ListContent(getContext(), mSlice); - SliceContent headerContent = lc.getHeader(); - mHasHeader = headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM); - List<SliceContent> subItems = new ArrayList<>(); - for (int i = 0; i < lc.getRowItems().size(); i++) { - SliceContent subItem = lc.getRowItems().get(i); - String itemUri = subItem.getSliceItem().getSlice().getUri().toString(); - // Filter out the action row - if (!KeyguardSliceProvider.KEYGUARD_ACTION_URI.equals(itemUri)) { - subItems.add(subItem); - } + void hideSlice() { + mTitle.setVisibility(GONE); + mRow.setVisibility(GONE); + mHasHeader = false; + if (mContentChangeListener != null) { + mContentChangeListener.run(); } + } + + Map<View, PendingIntent> showSlice(RowContent header, List<SliceContent> subItems) { + Trace.beginSection("KeyguardSliceView#showSlice"); + mHasHeader = header != null; + Map<View, PendingIntent> clickActions = new HashMap<>(); + if (!mHasHeader) { mTitle.setVisibility(GONE); } else { mTitle.setVisibility(VISIBLE); - RowContent header = lc.getHeader(); SliceItem mainTitle = header.getTitleItem(); CharSequence title = mainTitle != null ? mainTitle.getText() : null; mTitle.setText(title); if (header.getPrimaryAction() != null && header.getPrimaryAction().getAction() != null) { - mClickActions.put(mTitle, header.getPrimaryAction().getAction()); + clickActions.put(mTitle, header.getPrimaryAction().getAction()); } } @@ -265,7 +187,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe if (rc.getPrimaryAction() != null) { pendingIntent = rc.getPrimaryAction().getAction(); } - mClickActions.put(button, pendingIntent); + clickActions.put(button, pendingIntent); final SliceItem titleItem = rc.getTitleItem(); button.setText(titleItem == null ? null : titleItem.getText()); @@ -286,14 +208,14 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe } } button.setCompoundDrawables(iconDrawable, null, null, null); - button.setOnClickListener(this); + button.setOnClickListener(mOnClickListener); button.setClickable(pendingIntent != null); } // Removing old views for (int i = 0; i < mRow.getChildCount(); i++) { View child = mRow.getChildAt(i); - if (!mClickActions.containsKey(child)) { + if (!clickActions.containsKey(child)) { mRow.removeView(child); i--; } @@ -303,6 +225,8 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe mContentChangeListener.run(); } Trace.endSection(); + + return clickActions; } public void setDarkAmount(float darkAmount) { @@ -323,14 +247,6 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe } } - @Override - public void onClick(View v) { - final PendingIntent action = mClickActions.get(v); - if (action != null) { - mActivityStarter.startPendingIntentDismissingKeyguard(action); - } - } - /** * Runnable that gets invoked every time the title or the row visibility changes. * @param contentChangeListener The listener. @@ -339,43 +255,6 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe mContentChangeListener = contentChangeListener; } - /** - * LiveData observer lifecycle. - * @param slice the new slice content. - */ - @Override - public void onChanged(Slice slice) { - mSlice = slice; - showSlice(); - } - - @Override - public void onTuningChanged(String key, String newValue) { - setupUri(newValue); - } - - /** - * Sets the slice provider Uri. - */ - public void setupUri(String uriString) { - if (uriString == null) { - uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI; - } - - boolean wasObserving = false; - if (mLiveData != null && mLiveData.hasActiveObservers()) { - wasObserving = true; - mLiveData.removeObserver(this); - } - - mKeyguardSliceUri = Uri.parse(uriString); - mLiveData = SliceLiveData.fromUri(mContext, mKeyguardSliceUri); - - if (wasObserving) { - mLiveData.observeForever(this); - } - } - @VisibleForTesting int getTextColor() { return ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount); @@ -387,8 +266,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe updateTextColors(); } - @Override - public void onDensityOrFontScaleChanged() { + void onDensityOrFontScaleChanged() { mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.widget_icon_size); mIconSizeWithHeader = (int) mContext.getResources().getDimension(R.dimen.header_icon_size); mRowTextSize = mContext.getResources().getDimensionPixelSize( @@ -397,37 +275,21 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe R.dimen.header_row_font_size); } - public void refresh() { - Slice slice; - Trace.beginSection("KeyguardSliceView#refresh"); - // We can optimize performance and avoid binder calls when we know that we're bound - // to a Slice on the same process. - if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) { - KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance(); - if (instance != null) { - slice = instance.onBindSlice(mKeyguardSliceUri); - } else { - Log.w(TAG, "Keyguard slice not bound yet?"); - slice = null; - } - } else { - slice = SliceViewManager.getInstance(getContext()).bindSlice(mKeyguardSliceUri); - } - onChanged(slice); - Trace.endSection(); - } - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("KeyguardSliceView:"); - pw.println(" mClickActions: " + mClickActions); pw.println(" mTitle: " + (mTitle == null ? "null" : mTitle.getVisibility() == VISIBLE)); pw.println(" mRow: " + (mRow == null ? "null" : mRow.getVisibility() == VISIBLE)); pw.println(" mTextColor: " + Integer.toHexString(mTextColor)); pw.println(" mDarkAmount: " + mDarkAmount); - pw.println(" mSlice: " + mSlice); pw.println(" mHasHeader: " + mHasHeader); } + @Override + public void setOnClickListener(View.OnClickListener onClickListener) { + mOnClickListener = onClickListener; + mTitle.setOnClickListener(onClickListener); + } + public static class Row extends LinearLayout { /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java new file mode 100644 index 000000000000..2470b958a85f --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java @@ -0,0 +1,239 @@ +/* + * 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.keyguard; + +import static android.app.slice.Slice.HINT_LIST_ITEM; +import static android.view.Display.DEFAULT_DISPLAY; + +import android.app.PendingIntent; +import android.net.Uri; +import android.os.Trace; +import android.provider.Settings; +import android.util.Log; +import android.view.Display; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.Observer; +import androidx.slice.Slice; +import androidx.slice.SliceViewManager; +import androidx.slice.widget.ListContent; +import androidx.slice.widget.RowContent; +import androidx.slice.widget.SliceContent; +import androidx.slice.widget.SliceLiveData; + +import com.android.keyguard.dagger.KeyguardStatusViewScope; +import com.android.systemui.Dumpable; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.keyguard.KeyguardSliceProvider; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.tuner.TunerService; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +/** Controller for a {@link KeyguardSliceView}. */ +@KeyguardStatusViewScope +public class KeyguardSliceViewController implements Dumpable { + private static final String TAG = "KeyguardSliceViewCtrl"; + + private final KeyguardSliceView mView; + private final KeyguardStatusView mKeyguardStatusView; + private final ActivityStarter mActivityStarter; + private final ConfigurationController mConfigurationController; + private final TunerService mTunerService; + private final DumpManager mDumpManager; + private int mDisplayId; + private LiveData<Slice> mLiveData; + private Uri mKeyguardSliceUri; + private Slice mSlice; + private Map<View, PendingIntent> mClickActions; + + private final View.OnAttachStateChangeListener mOnAttachStateChangeListener = + new View.OnAttachStateChangeListener() { + + @Override + public void onViewAttachedToWindow(View v) { + + Display display = mView.getDisplay(); + if (display != null) { + mDisplayId = display.getDisplayId(); + } + mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI); + // Make sure we always have the most current slice + if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) { + mLiveData.observeForever(mObserver); + } + mConfigurationController.addCallback(mConfigurationListener); + mDumpManager.registerDumpable( + TAG + "@" + Integer.toHexString( + KeyguardSliceViewController.this.hashCode()), + KeyguardSliceViewController.this); + } + + @Override + public void onViewDetachedFromWindow(View v) { + + // TODO(b/117344873) Remove below work around after this issue be fixed. + if (mDisplayId == DEFAULT_DISPLAY) { + mLiveData.removeObserver(mObserver); + } + mTunerService.removeTunable(mTunable); + mConfigurationController.removeCallback(mConfigurationListener); + mDumpManager.unregisterDumpable(TAG); + } + }; + + TunerService.Tunable mTunable = (key, newValue) -> setupUri(newValue); + + ConfigurationController.ConfigurationListener mConfigurationListener = + new ConfigurationController.ConfigurationListener() { + @Override + public void onDensityOrFontScaleChanged() { + mView.onDensityOrFontScaleChanged(); + } + }; + + Observer<Slice> mObserver = new Observer<Slice>() { + @Override + public void onChanged(Slice slice) { + mSlice = slice; + showSlice(slice); + } + }; + + private View.OnClickListener mOnClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + final PendingIntent action = mClickActions.get(v); + if (action != null && mActivityStarter != null) { + mActivityStarter.startPendingIntentDismissingKeyguard(action); + } + } + }; + + @Inject + public KeyguardSliceViewController(KeyguardSliceView keyguardSliceView, + KeyguardStatusView keyguardStatusView, ActivityStarter activityStarter, + ConfigurationController configurationController, TunerService tunerService, + DumpManager dumpManager) { + mView = keyguardSliceView; + mKeyguardStatusView = keyguardStatusView; + mActivityStarter = activityStarter; + mConfigurationController = configurationController; + mTunerService = tunerService; + mDumpManager = dumpManager; + } + + /** Initialize the controller. */ + public void init() { + if (mView.isAttachedToWindow()) { + mOnAttachStateChangeListener.onViewAttachedToWindow(mView); + } + mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener); + mView.setOnClickListener(mOnClickListener); + // TODO: remove the line below. + mKeyguardStatusView.setKeyguardSliceViewController(this); + } + + /** + * Sets the slice provider Uri. + */ + public void setupUri(String uriString) { + if (uriString == null) { + uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI; + } + + boolean wasObserving = false; + if (mLiveData != null && mLiveData.hasActiveObservers()) { + wasObserving = true; + mLiveData.removeObserver(mObserver); + } + + mKeyguardSliceUri = Uri.parse(uriString); + mLiveData = SliceLiveData.fromUri(mView.getContext(), mKeyguardSliceUri); + + if (wasObserving) { + mLiveData.observeForever(mObserver); + } + } + + /** + * Update contents of the view. + */ + public void refresh() { + Slice slice; + Trace.beginSection("KeyguardSliceViewController#refresh"); + // We can optimize performance and avoid binder calls when we know that we're bound + // to a Slice on the same process. + if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) { + KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance(); + if (instance != null) { + slice = instance.onBindSlice(mKeyguardSliceUri); + } else { + Log.w(TAG, "Keyguard slice not bound yet?"); + slice = null; + } + } else { + // TODO: Make SliceViewManager injectable + slice = SliceViewManager.getInstance(mView.getContext()).bindSlice(mKeyguardSliceUri); + } + mObserver.onChanged(slice); + Trace.endSection(); + } + + void showSlice(Slice slice) { + Trace.beginSection("KeyguardSliceViewController#showSlice"); + if (slice == null) { + mView.hideSlice(); + Trace.endSection(); + return; + } + + ListContent lc = new ListContent(slice); + RowContent headerContent = lc.getHeader(); + boolean hasHeader = + headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM); + + List<SliceContent> subItems = lc.getRowItems().stream().filter(sliceContent -> { + String itemUri = sliceContent.getSliceItem().getSlice().getUri().toString(); + // Filter out the action row + return !KeyguardSliceProvider.KEYGUARD_ACTION_URI.equals(itemUri); + }).collect(Collectors.toList()); + + + mClickActions = mView.showSlice(hasHeader ? headerContent : null, subItems); + + Trace.endSection(); + } + + + @Override + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + pw.println(" mSlice: " + mSlice); + pw.println(" mClickActions: " + mClickActions); + + mKeyguardStatusView.dump(fd, pw, args); + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 4c6aafb0058a..6e111745627f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -32,7 +32,6 @@ import android.util.Slog; import android.util.TypedValue; import android.view.View; import android.widget.GridLayout; -import android.widget.LinearLayout; import android.widget.TextView; import androidx.core.graphics.ColorUtils; @@ -56,7 +55,6 @@ public class KeyguardStatusView extends GridLayout implements private final LockPatternUtils mLockPatternUtils; private final IActivityManager mIActivityManager; - private LinearLayout mStatusViewContainer; private TextView mLogoutView; private KeyguardClockSwitch mClockView; private TextView mOwnerInfo; @@ -64,6 +62,7 @@ public class KeyguardStatusView extends GridLayout implements private View mNotificationIcons; private Runnable mPendingMarqueeStart; private Handler mHandler; + private KeyguardSliceViewController mKeyguardSliceViewController; private boolean mPulsing; private float mDarkAmount = 0; @@ -179,7 +178,6 @@ public class KeyguardStatusView extends GridLayout implements @Override protected void onFinishInflate() { super.onFinishInflate(); - mStatusViewContainer = findViewById(R.id.status_view_container); mLogoutView = findViewById(R.id.logout); mNotificationIcons = findViewById(R.id.clock_notification_icon_container); if (mLogoutView != null) { @@ -250,7 +248,7 @@ public class KeyguardStatusView extends GridLayout implements public void dozeTimeTick() { refreshTime(); - mKeyguardSlice.refresh(); + mKeyguardSliceViewController.refresh(); } private void refreshTime() { @@ -456,4 +454,9 @@ public class KeyguardStatusView extends GridLayout implements Log.e(TAG, "Failed to logout user", re); } } + + // TODO: remove this method when a controller is available. + void setKeyguardSliceViewController(KeyguardSliceViewController keyguardSliceViewController) { + mKeyguardSliceViewController = keyguardSliceViewController; + } } diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java new file mode 100644 index 000000000000..21ccff707d34 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java @@ -0,0 +1,39 @@ +/* + * 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.keyguard.dagger; + +import com.android.keyguard.KeyguardClockSwitchController; +import com.android.keyguard.KeyguardStatusView; + +import dagger.BindsInstance; +import dagger.Subcomponent; + +/** + * Subcomponent for helping work with KeyguardStatusView and its children. + */ +@Subcomponent(modules = {KeyguardStatusViewModule.class}) +@KeyguardStatusViewScope +public interface KeyguardStatusViewComponent { + /** Simple factory for {@link KeyguardStatusViewComponent}. */ + @Subcomponent.Factory + interface Factory { + KeyguardStatusViewComponent build(@BindsInstance KeyguardStatusView presentation); + } + + /** Builds a {@link com.android.keyguard.KeyguardClockSwitchController}. */ + KeyguardClockSwitchController getKeyguardClockSwitchController(); +} diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java new file mode 100644 index 000000000000..1d51e5925de8 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java @@ -0,0 +1,39 @@ +/* + * 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.keyguard.dagger; + +import com.android.keyguard.KeyguardClockSwitch; +import com.android.keyguard.KeyguardSliceView; +import com.android.keyguard.KeyguardStatusView; +import com.android.systemui.R; + +import dagger.Module; +import dagger.Provides; + +/** Dagger module for {@link KeyguardStatusViewComponent}. */ +@Module +public abstract class KeyguardStatusViewModule { + @Provides + static KeyguardClockSwitch getKeyguardClockSwitch(KeyguardStatusView keyguardPresentation) { + return keyguardPresentation.findViewById(R.id.keyguard_clock_container); + } + + @Provides + static KeyguardSliceView getKeyguardSliceView(KeyguardClockSwitch keyguardClockSwitch) { + return keyguardClockSwitch.findViewById(R.id.keyguard_status_area); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java index 114c30e625aa..880822aa7343 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java @@ -14,17 +14,19 @@ * limitations under the License. */ -package com.android.systemui.pip.phone.dagger; +package com.android.keyguard.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Retention; -import javax.inject.Qualifier; +import javax.inject.Scope; -@Qualifier +/** + * Scope annotation for singleton items within the StatusBarComponent. + */ @Documented @Retention(RUNTIME) -public @interface PipMenuActivityClass { -} +@Scope +public @interface KeyguardStatusViewScope {} diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index d1149d37d431..f4c865e1d131 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -21,7 +21,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.settings.UserContextProvider; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -38,7 +38,7 @@ import java.util.Set; * * NOTE: Clients of this class should take care to pass in the correct user context when querying * settings, otherwise you will always read/write for user 0 which is almost never what you want. - * See {@link CurrentUserContextTracker} for a simple way to get the current context + * See {@link UserContextProvider} for a simple way to get the current context */ public final class Prefs { private Prefs() {} // no instantation diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 47066a05b9b8..e91284b546db 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -16,6 +16,8 @@ package com.android.systemui; +import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -696,14 +698,15 @@ public class SwipeHelper implements Gefingerpoken { float translation = getTranslation(mCurrView); return ev.getActionMasked() == MotionEvent.ACTION_UP && !mFalsingManager.isUnlockingDisabled() - && !isFalseGesture(ev) && (swipedFastEnough() || swipedFarEnough()) + && !isFalseGesture() && (swipedFastEnough() || swipedFarEnough()) && mCallback.canChildBeDismissedInDirection(mCurrView, translation > 0); } - public boolean isFalseGesture(MotionEvent ev) { + /** Returns true if the gesture should be rejected. */ + public boolean isFalseGesture() { boolean falsingDetected = mCallback.isAntiFalsingNeeded(); if (mFalsingManager.isClassifierEnabled()) { - falsingDetected = falsingDetected && mFalsingManager.isFalseTouch(); + falsingDetected = falsingDetected && mFalsingManager.isFalseTouch(NOTIFICATION_DISMISS); } else { falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold; } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index f5c364947a2f..941fd6f320e2 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -38,6 +38,7 @@ import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; /** @@ -83,11 +84,16 @@ public class SystemUIFactory { public SystemUIFactory() {} - private void init(Context context) { + private void init(Context context) throws ExecutionException, InterruptedException { mRootComponent = buildGlobalRootComponent(context); + // Stand up WMComponent mWMComponent = mRootComponent.getWMComponentBuilder().build(); - // TODO: use WMComponent to pass APIs into the SysUIComponent. - mSysUIComponent = mRootComponent.getSysUIComponent().build(); + + // And finally, retrieve whatever SysUI needs from WMShell and build SysUI. + // TODO: StubAPIClass is just a placeholder. + mSysUIComponent = mRootComponent.getSysUIComponent() + .setStubAPIClass(mWMComponent.createStubAPIClass()) + .build(); // Every other part of our codebase currently relies on Dependency, so we // really need to ensure the Dependency gets initialized early on. @@ -101,10 +107,15 @@ public class SystemUIFactory { .build(); } + public GlobalRootComponent getRootComponent() { return mRootComponent; } + public WMComponent getWMComponent() { + return mWMComponent; + } + public SysUIComponent getSysUIComponent() { return mSysUIComponent; } diff --git a/packages/SystemUI/src/com/android/systemui/appops/dagger/AppOpsModule.java b/packages/SystemUI/src/com/android/systemui/appops/dagger/AppOpsModule.java new file mode 100644 index 000000000000..d4cc3f37b8dd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/appops/dagger/AppOpsModule.java @@ -0,0 +1,32 @@ +/* + * 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.appops.dagger; + +import com.android.systemui.appops.AppOpsController; +import com.android.systemui.appops.AppOpsControllerImpl; + +import dagger.Binds; +import dagger.Module; + +/** Dagger Module for code in the appops package. */ +@Module +public interface AppOpsModule { + /** */ + @Binds + AppOpsController provideAppOpsController(AppOpsControllerImpl controllerImpl); + +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java index 646e62062dfb..6961b45c3c37 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java @@ -70,7 +70,7 @@ public class FalsingManagerFake implements FalsingManager { } @Override - public boolean isFalseTouch() { + public boolean isFalseTouch(@Classifier.InteractionType int interactionType) { return mIsFalseTouch; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java index cc64fb53f15f..decaec10e572 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java @@ -262,7 +262,7 @@ public class FalsingManagerImpl implements FalsingManager { /** * @return true if the classifier determined that this is not a human interacting with the phone */ - public boolean isFalseTouch() { + public boolean isFalseTouch(@Classifier.InteractionType int interactionType) { if (FalsingLog.ENABLED) { // We're getting some false wtfs from touches that happen after the device went // to sleep. Only report missing sessions that happen when the device is interactive. diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index 83b6df3e701b..2c31862e9b79 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -187,8 +187,8 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { } @Override - public boolean isFalseTouch() { - return mInternalFalsingManager.isFalseTouch(); + public boolean isFalseTouch(@Classifier.InteractionType int interactionType) { + return mInternalFalsingManager.isFalseTouch(interactionType); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java index a50f9ce9713b..9d847ca62465 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -189,7 +189,8 @@ public class BrightLineFalsingManager implements FalsingManager { } @Override - public boolean isFalseTouch() { + public boolean isFalseTouch(@Classifier.InteractionType int interactionType) { + mDataProvider.setInteractionType(interactionType); if (!mDataProvider.isDirty()) { return mPreviousResult; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java index ea46441c8fbe..8d067489a8cc 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java @@ -116,7 +116,10 @@ public class FalsingDataProvider { * interactionType is defined by {@link com.android.systemui.classifier.Classifier}. */ final void setInteractionType(@Classifier.InteractionType int interactionType) { - this.mInteractionType = interactionType; + if (mInteractionType != interactionType) { + mInteractionType = interactionType; + mDirty = true; + } } public boolean isDirty() { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java deleted file mode 100644 index e2a6d6c51d4d..000000000000 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * 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; - -import com.android.systemui.ActivityStarterDelegate; -import com.android.systemui.appops.AppOpsController; -import com.android.systemui.appops.AppOpsControllerImpl; -import com.android.systemui.classifier.FalsingManagerProxy; -import com.android.systemui.controls.dagger.ControlsModule; -import com.android.systemui.globalactions.GlobalActionsComponent; -import com.android.systemui.globalactions.GlobalActionsImpl; -import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.DarkIconDispatcher; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.GlobalActions; -import com.android.systemui.plugins.VolumeDialogController; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.power.PowerNotificationWarnings; -import com.android.systemui.power.PowerUI; -import com.android.systemui.qs.QSHost; -import com.android.systemui.qs.QSTileHost; -import com.android.systemui.statusbar.NotificationRemoteInputManager; -import com.android.systemui.statusbar.StatusBarStateControllerImpl; -import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl; -import com.android.systemui.statusbar.phone.ManagedProfileController; -import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; -import com.android.systemui.statusbar.phone.StatusBarIconController; -import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; -import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; -import com.android.systemui.statusbar.policy.BluetoothController; -import com.android.systemui.statusbar.policy.BluetoothControllerImpl; -import com.android.systemui.statusbar.policy.CastController; -import com.android.systemui.statusbar.policy.CastControllerImpl; -import com.android.systemui.statusbar.policy.ExtensionController; -import com.android.systemui.statusbar.policy.ExtensionControllerImpl; -import com.android.systemui.statusbar.policy.FlashlightController; -import com.android.systemui.statusbar.policy.FlashlightControllerImpl; -import com.android.systemui.statusbar.policy.HotspotController; -import com.android.systemui.statusbar.policy.HotspotControllerImpl; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl; -import com.android.systemui.statusbar.policy.LocationController; -import com.android.systemui.statusbar.policy.LocationControllerImpl; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkControllerImpl; -import com.android.systemui.statusbar.policy.NextAlarmController; -import com.android.systemui.statusbar.policy.NextAlarmControllerImpl; -import com.android.systemui.statusbar.policy.RotationLockController; -import com.android.systemui.statusbar.policy.RotationLockControllerImpl; -import com.android.systemui.statusbar.policy.SecurityController; -import com.android.systemui.statusbar.policy.SecurityControllerImpl; -import com.android.systemui.statusbar.policy.SensorPrivacyController; -import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl; -import com.android.systemui.statusbar.policy.UserInfoController; -import com.android.systemui.statusbar.policy.UserInfoControllerImpl; -import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.systemui.statusbar.policy.ZenModeControllerImpl; -import com.android.systemui.tuner.TunerService; -import com.android.systemui.tuner.TunerServiceImpl; -import com.android.systemui.util.RingerModeTracker; -import com.android.systemui.util.RingerModeTrackerImpl; -import com.android.systemui.volume.VolumeComponent; -import com.android.systemui.volume.VolumeDialogComponent; -import com.android.systemui.volume.VolumeDialogControllerImpl; - -import dagger.Binds; -import dagger.Module; - -/** - * Maps interfaces to implementations for use with Dagger. - */ -@Module(includes = {ControlsModule.class}) -public abstract class DependencyBinder { - - /** - */ - @Binds - public abstract ActivityStarter provideActivityStarter(ActivityStarterDelegate delegate); - - /** - */ - @Binds - public abstract BluetoothController provideBluetoothController( - BluetoothControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract GlobalActions provideGlobalActions(GlobalActionsImpl controllerImpl); - - /** - */ - @Binds - public abstract GlobalActions.GlobalActionsManager provideGlobalActionsManager( - GlobalActionsComponent controllerImpl); - - /** - */ - @Binds - public abstract LocationController provideLocationController( - LocationControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract RotationLockController provideRotationLockController( - RotationLockControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract NetworkController provideNetworkController( - NetworkControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract ZenModeController provideZenModeController( - ZenModeControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract HotspotController provideHotspotController( - HotspotControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract AppOpsController provideAppOpsController( - AppOpsControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract NotificationRemoteInputManager.Callback provideNotificationRemoteInputManager( - StatusBarRemoteInputCallback callbackImpl); - - /** - */ - @Binds - public abstract CastController provideCastController(CastControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract FlashlightController provideFlashlightController( - FlashlightControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract KeyguardStateController provideKeyguardMonitor( - KeyguardStateControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract UserInfoController provideUserInfoContrller( - UserInfoControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract ManagedProfileController provideManagedProfileController( - ManagedProfileControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract NextAlarmController provideNextAlarmController( - NextAlarmControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract SecurityController provideSecurityController( - SecurityControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract TunerService provideTunerService(TunerServiceImpl controllerImpl); - - /** - */ - @Binds - public abstract DarkIconDispatcher provideDarkIconDispatcher( - DarkIconDispatcherImpl controllerImpl); - - /** - */ - @Binds - public abstract StatusBarStateController provideStatusBarStateController( - StatusBarStateControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract SysuiStatusBarStateController providesSysuiStatusBarStateController( - StatusBarStateControllerImpl statusBarStateControllerImpl); - - /** - */ - @Binds - public abstract StatusBarIconController provideStatusBarIconController( - StatusBarIconControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract ExtensionController provideExtensionController( - ExtensionControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract VolumeDialogController provideVolumeDialogController( - VolumeDialogControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl); - - /** - */ - @Binds - public abstract SensorPrivacyController provideSensorPrivacyControllerImpl( - SensorPrivacyControllerImpl controllerImpl); - - /** - */ - @Binds - public abstract QSHost provideQsHost(QSTileHost controllerImpl); - - /** - */ - @Binds - public abstract FalsingManager provideFalsingManager(FalsingManagerProxy falsingManagerImpl); - - /** - */ - @Binds - public abstract VolumeComponent provideVolumeComponent( - VolumeDialogComponent volumeDialogComponent); - - /** - */ - @Binds - public abstract RingerModeTracker provideRingerModeTracker( - RingerModeTrackerImpl ringerModeTrackerImpl); -} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 8a67e968ae13..0dd9488f1d3e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -18,6 +18,8 @@ package com.android.systemui.dagger; import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; +import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.app.INotificationManager; import android.content.Context; import android.content.SharedPreferences; @@ -26,6 +28,7 @@ import android.os.Handler; 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; @@ -39,6 +42,7 @@ import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.util.NotificationMessagingUtil; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; +import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.Prefs; import com.android.systemui.accessibility.ModeSwitchesController; import com.android.systemui.accessibility.SystemActions; @@ -88,7 +92,7 @@ import dagger.Provides; * Provides dependencies for the root component of sysui injection. * * Only SystemUI owned classes and instances should go in here. Other, framework-owned classes - * should go in {@link SystemServicesModule}. + * should go in {@link FrameworkServicesModule}. * * See SystemUI/docs/dagger.md */ @@ -163,6 +167,15 @@ public class DependencyProvider { } + @SuppressLint("MissingPermission") + @SysUISingleton + @Provides + @Nullable + static LocalBluetoothManager provideLocalBluetoothController(Context context, + @Background Handler bgHandler) { + return LocalBluetoothManager.create(context, bgHandler, UserHandle.ALL); + } + /** */ @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index ceb9aeee1d3b..66063a87a70d 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -17,7 +17,6 @@ package com.android.systemui.dagger; import android.annotation.Nullable; -import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.AlarmManager; @@ -49,10 +48,8 @@ import android.net.ConnectivityManager; import android.net.NetworkScoreManager; import android.net.wifi.WifiManager; import android.os.BatteryStats; -import android.os.Handler; import android.os.PowerManager; import android.os.ServiceManager; -import android.os.UserHandle; import android.os.UserManager; import android.os.Vibrator; import android.service.dreams.DreamService; @@ -68,12 +65,12 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.app.IBatteryStats; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.LatencyTracker; -import com.android.settingslib.bluetooth.LocalBluetoothManager; -import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.PackageManagerWrapper; +import javax.inject.Singleton; + import dagger.Module; import dagger.Provides; @@ -81,51 +78,51 @@ import dagger.Provides; * Provides Non-SystemUI, Framework-Owned instances to the dependency graph. */ @Module -public class SystemServicesModule { +public class FrameworkServicesModule { @Provides - @SysUISingleton + @Singleton static AccessibilityManager provideAccessibilityManager(Context context) { return context.getSystemService(AccessibilityManager.class); } @Provides - @SysUISingleton + @Singleton static ActivityManager provideActivityManager(Context context) { return context.getSystemService(ActivityManager.class); } - @SysUISingleton @Provides + @Singleton static AlarmManager provideAlarmManager(Context context) { return context.getSystemService(AlarmManager.class); } @Provides - @SysUISingleton + @Singleton static AudioManager provideAudioManager(Context context) { return context.getSystemService(AudioManager.class); } @Provides - @SysUISingleton + @Singleton static ColorDisplayManager provideColorDisplayManager(Context context) { return context.getSystemService(ColorDisplayManager.class); } @Provides - @SysUISingleton + @Singleton static ConnectivityManager provideConnectivityManagager(Context context) { return context.getSystemService(ConnectivityManager.class); } @Provides - @SysUISingleton + @Singleton static ContentResolver provideContentResolver(Context context) { return context.getContentResolver(); } @Provides - @SysUISingleton + @Singleton static DevicePolicyManager provideDevicePolicyManager(Context context) { return context.getSystemService(DevicePolicyManager.class); } @@ -137,39 +134,39 @@ public class SystemServicesModule { } @Provides - @SysUISingleton + @Singleton static DisplayManager provideDisplayManager(Context context) { return context.getSystemService(DisplayManager.class); } - @SysUISingleton @Provides + @Singleton static IActivityManager provideIActivityManager() { return ActivityManager.getService(); } - @SysUISingleton @Provides + @Singleton static IActivityTaskManager provideIActivityTaskManager() { return ActivityTaskManager.getService(); } @Provides - @SysUISingleton + @Singleton static IBatteryStats provideIBatteryStats() { return IBatteryStats.Stub.asInterface( ServiceManager.getService(BatteryStats.SERVICE_NAME)); } @Provides - @SysUISingleton + @Singleton static IDreamManager provideIDreamManager() { return IDreamManager.Stub.asInterface( ServiceManager.checkService(DreamService.DREAM_SERVICE)); } @Provides - @SysUISingleton + @Singleton @Nullable static FaceManager provideFaceManager(Context context) { return context.getSystemService(FaceManager.class); @@ -177,13 +174,13 @@ public class SystemServicesModule { } @Provides - @SysUISingleton + @Singleton static IPackageManager provideIPackageManager() { return IPackageManager.Stub.asInterface(ServiceManager.getService("package")); } - @SysUISingleton @Provides + @Singleton static IStatusBarService provideIStatusBarService() { return IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); @@ -196,39 +193,30 @@ public class SystemServicesModule { ServiceManager.getService(Context.WALLPAPER_SERVICE)); } - @SysUISingleton @Provides + @Singleton static IWindowManager provideIWindowManager() { return WindowManagerGlobal.getWindowManagerService(); } - @SysUISingleton @Provides + @Singleton static KeyguardManager provideKeyguardManager(Context context) { return context.getSystemService(KeyguardManager.class); } - @SysUISingleton @Provides + @Singleton static LatencyTracker provideLatencyTracker(Context context) { return LatencyTracker.getInstance(context); } - @SysUISingleton @Provides + @Singleton static LauncherApps provideLauncherApps(Context context) { return context.getSystemService(LauncherApps.class); } - @SuppressLint("MissingPermission") - @SysUISingleton - @Provides - @Nullable - static LocalBluetoothManager provideLocalBluetoothController(Context context, - @Background Handler bgHandler) { - return LocalBluetoothManager.create(context, bgHandler, UserHandle.ALL); - } - @Provides static MediaRouter2Manager provideMediaRouter2Manager(Context context) { return MediaRouter2Manager.getInstance(context); @@ -240,32 +228,32 @@ public class SystemServicesModule { } @Provides - @SysUISingleton + @Singleton static NetworkScoreManager provideNetworkScoreManager(Context context) { return context.getSystemService(NetworkScoreManager.class); } - @SysUISingleton @Provides + @Singleton static NotificationManager provideNotificationManager(Context context) { return context.getSystemService(NotificationManager.class); } - @SysUISingleton @Provides + @Singleton static PackageManager providePackageManager(Context context) { return context.getPackageManager(); } - @SysUISingleton @Provides + @Singleton static PackageManagerWrapper providePackageManagerWrapper() { return PackageManagerWrapper.getInstance(); } /** */ - @SysUISingleton @Provides + @Singleton static PowerManager providePowerManager(Context context) { return context.getSystemService(PowerManager.class); } @@ -277,57 +265,63 @@ public class SystemServicesModule { } @Provides - @SysUISingleton + @Singleton + static RoleManager provideRoleManager(Context context) { + return context.getSystemService(RoleManager.class); + } + + @Provides + @Singleton static SensorManager providesSensorManager(Context context) { return context.getSystemService(SensorManager.class); } - @SysUISingleton @Provides + @Singleton static SensorPrivacyManager provideSensorPrivacyManager(Context context) { return context.getSystemService(SensorPrivacyManager.class); } - @SysUISingleton @Provides + @Singleton static ShortcutManager provideShortcutManager(Context context) { return context.getSystemService(ShortcutManager.class); } @Provides - @SysUISingleton + @Singleton @Nullable static TelecomManager provideTelecomManager(Context context) { return context.getSystemService(TelecomManager.class); } @Provides - @SysUISingleton + @Singleton static TelephonyManager provideTelephonyManager(Context context) { return context.getSystemService(TelephonyManager.class); } @Provides - @SysUISingleton + @Singleton static TrustManager provideTrustManager(Context context) { return context.getSystemService(TrustManager.class); } @Provides - @SysUISingleton + @Singleton @Nullable static Vibrator provideVibrator(Context context) { return context.getSystemService(Vibrator.class); } @Provides - @SysUISingleton + @Singleton static ViewConfiguration provideViewConfiguration(Context context) { return ViewConfiguration.get(context); } @Provides - @SysUISingleton + @Singleton static UserManager provideUserManager(Context context) { return context.getSystemService(UserManager.class); } @@ -338,21 +332,15 @@ public class SystemServicesModule { } @Provides - @SysUISingleton + @Singleton @Nullable static WifiManager provideWifiManager(Context context) { return context.getSystemService(WifiManager.class); } - @SysUISingleton @Provides + @Singleton static WindowManager provideWindowManager(Context context) { return context.getSystemService(WindowManager.class); } - - @Provides - @SysUISingleton - static RoleManager provideRoleManager(Context context) { - return context.getSystemService(RoleManager.class); - } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java index 553655bf672c..c5dc8cccfdf4 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java @@ -16,63 +16,27 @@ package com.android.systemui.dagger; -import android.content.Context; -import android.content.SharedPreferences; -import android.hardware.display.AmbientDisplayConfiguration; -import android.util.DisplayMetrics; -import android.view.Choreographer; - -import com.android.systemui.Prefs; -import com.android.systemui.dagger.qualifiers.Main; - -import javax.inject.Singleton; +import com.android.systemui.util.concurrency.GlobalConcurrencyModule; import dagger.Module; -import dagger.Provides; /** - * Supplies globally scoped instances. + * Supplies globally scoped instances that should be available in all versions of SystemUI * * Providers in this module will be accessible to both WMComponent and SysUIComponent scoped * classes. They are in here because they are either needed globally or are inherently universal * to the application. * * Note that just because a class might be used by both WM and SysUI does not necessarily mean that - * it should got into this module. If WM and SysUI might need the class for different purposes + * it should go into this module. If WM and SysUI might need the class for different purposes * or different semantics, it may make sense to ask them to supply their own. Something like * threading and concurrency provide a good example. Both components need * Threads/Handlers/Executors, but they need separate instances of them in many cases. * * Please use discretion when adding things to the global scope. */ -@Module +@Module(includes = { + FrameworkServicesModule.class, + GlobalConcurrencyModule.class}) public class GlobalModule { - /** */ - @Provides - @Main - public SharedPreferences provideSharePreferences(Context context) { - return Prefs.get(context); - } - - /** */ - @Provides - public AmbientDisplayConfiguration provideAmbientDisplayConfiguration(Context context) { - return new AmbientDisplayConfiguration(context); - } - - /** */ - @Provides - @Singleton - public Choreographer providesChoreographer() { - return Choreographer.getInstance(); - } - - /** */ - @Provides - @Singleton - public DisplayMetrics provideDisplayMetrics(Context context) { - DisplayMetrics displayMetrics = new DisplayMetrics(); - context.getDisplay().getMetrics(displayMetrics); - return displayMetrics; - } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java index 3d7c8ad4c43e..00fdf55b28e0 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java @@ -18,6 +18,8 @@ package com.android.systemui.dagger; import android.content.Context; +import com.android.systemui.util.concurrency.ThreadFactory; + import javax.inject.Singleton; import dagger.BindsInstance; @@ -28,6 +30,7 @@ import dagger.Component; */ @Singleton @Component(modules = { + GlobalModule.class, SysUISubcomponentModule.class, WMModule.class}) public interface GlobalRootComponent { @@ -52,4 +55,9 @@ public interface GlobalRootComponent { * Builder for a SysuiComponent. */ SysUIComponent.Builder getSysUIComponent(); + + /** + * Build a {@link ThreadFactory}. + */ + ThreadFactory createThreadFactory(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java b/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java new file mode 100644 index 000000000000..406981d0c4ad --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java @@ -0,0 +1,74 @@ +/* + * 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.dagger; + +import com.android.systemui.ActivityStarterDelegate; +import com.android.systemui.classifier.FalsingManagerProxy; +import com.android.systemui.globalactions.GlobalActionsComponent; +import com.android.systemui.globalactions.GlobalActionsImpl; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.DarkIconDispatcher; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.GlobalActions; +import com.android.systemui.plugins.VolumeDialogController; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; +import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl; +import com.android.systemui.volume.VolumeDialogControllerImpl; + +import dagger.Binds; +import dagger.Module; + +/** + * Module for binding Plugin implementations. + * + * TODO(b/166258224): Many of these should be moved closer to their implementations. + */ +@Module +public interface PluginModule { + + /** */ + @Binds + ActivityStarter provideActivityStarter(ActivityStarterDelegate delegate); + + /** */ + @Binds + DarkIconDispatcher provideDarkIconDispatcher(DarkIconDispatcherImpl controllerImpl); + + /** */ + @Binds + FalsingManager provideFalsingManager(FalsingManagerProxy falsingManagerImpl); + + /** */ + @Binds + GlobalActions provideGlobalActions(GlobalActionsImpl controllerImpl); + + /** */ + @Binds + GlobalActions.GlobalActionsManager provideGlobalActionsManager( + GlobalActionsComponent controllerImpl); + + /** */ + @Binds + StatusBarStateController provideStatusBarStateController( + StatusBarStateControllerImpl controllerImpl); + + /** */ + @Binds + VolumeDialogController provideVolumeDialogController(VolumeDialogControllerImpl controllerImpl); + +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index b606201cc803..2622593880ba 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -26,6 +26,7 @@ import com.android.systemui.pip.phone.dagger.PipModule; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.InjectionInflationController; +import dagger.BindsInstance; import dagger.Subcomponent; /** @@ -35,9 +36,7 @@ import dagger.Subcomponent; @Subcomponent(modules = { DefaultComponentBinder.class, DependencyProvider.class, - DependencyBinder.class, PipModule.class, - SystemServicesModule.class, SystemUIBinder.class, SystemUIModule.class, SystemUIDefaultModule.class}) @@ -48,6 +47,9 @@ public interface SysUIComponent { */ @Subcomponent.Builder interface Builder { + @BindsInstance + Builder setStubAPIClass(WMComponent.StubAPIClass stubAPIClass); + SysUIComponent build(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index e985e3d7ef90..99a3a9119f89 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -18,12 +18,15 @@ package com.android.systemui.dagger; import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; +import com.android.systemui.appops.dagger.AppOpsModule; import com.android.systemui.assist.AssistModule; +import com.android.systemui.controls.dagger.ControlsModule; import com.android.systemui.demomode.dagger.DemoModeModule; import com.android.systemui.doze.dagger.DozeComponent; import com.android.systemui.fragments.FragmentService; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.model.SysUiState; +import com.android.systemui.power.dagger.PowerModule; import com.android.systemui.recents.Recents; import com.android.systemui.screenshot.dagger.ScreenshotModule; import com.android.systemui.settings.dagger.SettingsModule; @@ -37,11 +40,15 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfC import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.util.concurrency.ConcurrencyModule; +import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule; +import com.android.systemui.tuner.dagger.TunerModule; +import com.android.systemui.util.concurrency.SysUIConcurrencyModule; +import com.android.systemui.util.dagger.UtilModule; import com.android.systemui.util.sensors.SensorModule; import com.android.systemui.util.settings.SettingsUtilModule; import com.android.systemui.util.time.SystemClock; import com.android.systemui.util.time.SystemClockImpl; +import com.android.systemui.volume.dagger.VolumeModule; import dagger.Binds; import dagger.BindsOptionalOf; @@ -53,15 +60,23 @@ import dagger.Provides; * implementation. */ @Module(includes = { + AppOpsModule.class, AssistModule.class, - ConcurrencyModule.class, + ControlsModule.class, DemoModeModule.class, LogModule.class, PeopleHubModule.class, + PowerModule.class, + PluginModule.class, ScreenshotModule.class, SensorModule.class, SettingsModule.class, - SettingsUtilModule.class + SettingsUtilModule.class, + StatusBarPolicyModule.class, + SysUIConcurrencyModule.class, + TunerModule.class, + UtilModule.class, + VolumeModule.class }, subcomponents = {StatusBarComponent.class, NotificationRowComponent.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index 929b61a3421c..ad90eff3c969 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -16,6 +16,8 @@ package com.android.systemui.dagger; +import javax.inject.Inject; + import dagger.Subcomponent; /** @@ -32,4 +34,19 @@ public interface WMComponent { interface Builder { WMComponent build(); } + + + /** + * Example class used for passing an API to SysUI from WMShell. + * + * TODO: Remove this once real WM classes are ready to go. + **/ + @WMSingleton + class StubAPIClass { + @Inject + StubAPIClass() {} + } + + /** Create a StubAPIClass. */ + StubAPIClass createStubAPIClass(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index e38dce05a32e..8364b486c8d7 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -262,11 +262,11 @@ public class DozeTriggers implements DozeMachine.Part { onWakeScreen(wakeEvent, mMachine.isExecutingTransition() ? null : mMachine.getState()); } else if (isLongPress) { requestPulse(pulseReason, true /* alreadyPerformedProxCheck */, - null /* onPulseSupressedListener */); + null /* onPulseSuppressedListener */); } else if (isWakeLockScreen) { if (wakeEvent) { requestPulse(pulseReason, true /* alreadyPerformedProxCheck */, - null /* onPulseSupressedListener */); + null /* onPulseSuppressedListener */); } } else { proximityCheckThenCall((result) -> { @@ -536,7 +536,7 @@ public class DozeTriggers implements DozeMachine.Part { if (PULSE_ACTION.equals(intent.getAction())) { if (DozeMachine.DEBUG) Log.d(TAG, "Received pulse intent"); requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */ - null /* onPulseSupressedListener */); + null /* onPulseSuppressedListener */); } if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) { mMachine.requestState(DozeMachine.State.FINISH); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index d213ac1132bb..3e64749c0dce 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -130,7 +130,7 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; -import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -252,7 +252,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final RingerModeTracker mRingerModeTracker; private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms private Handler mMainHandler; - private CurrentUserContextTracker mCurrentUserContextTracker; + private UserContextProvider mUserContextProvider; @VisibleForTesting boolean mShowLockScreenCardsAndControls = false; @@ -313,7 +313,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, UiEventLogger uiEventLogger, RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler, ControlsComponent controlsComponent, - CurrentUserContextTracker currentUserContextTracker) { + UserContextProvider userContextProvider) { mContext = context; mWindowManagerFuncs = windowManagerFuncs; mAudioManager = audioManager; @@ -342,7 +342,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mControlsControllerOptional = controlsComponent.getControlsController(); mSysUiState = sysUiState; mMainHandler = handler; - mCurrentUserContextTracker = currentUserContextTracker; + mUserContextProvider = userContextProvider; // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -436,7 +436,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, String[] preferredControlsPackages = mContext.getResources() .getStringArray(com.android.systemui.R.array.config_controlsPreferredPackages); - SharedPreferences prefs = mCurrentUserContextTracker.getCurrentUserContext() + SharedPreferences prefs = mUserContextProvider.getUserContext() .getSharedPreferences(PREFS_CONTROLS_FILE, Context.MODE_PRIVATE); Set<String> seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, Collections.emptySet()); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 6214a6448287..33407918f938 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -89,15 +89,14 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.dagger.KeyguardModule; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.DeviceConfigProxy; -import com.android.systemui.util.InjectionInflationController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -228,7 +227,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { /** TrustManager for letting it know when we change visibility */ private final TrustManager mTrustManager; - private final InjectionInflationController mInjectionInflationController; /** * Used to keep the device awake while to ensure the keyguard finishes opening before @@ -345,7 +343,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { /** * For managing external displays */ - private KeyguardDisplayManager mKeyguardDisplayManager; + private final KeyguardDisplayManager mKeyguardDisplayManager; private final ArrayList<IKeyguardStateCallback> mKeyguardStateCallbacks = new ArrayList<>(); @@ -724,7 +722,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { TrustManager trustManager, DeviceConfigProxy deviceConfig, NavigationModeController navigationModeController, - InjectionInflationController injectionInflationController) { + KeyguardDisplayManager keyguardDisplayManager) { super(context); mFalsingManager = falsingManager; mLockPatternUtils = lockPatternUtils; @@ -735,7 +733,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { mUpdateMonitor = keyguardUpdateMonitor; mPM = powerManager; mTrustManager = trustManager; - mInjectionInflationController = injectionInflationController; + mKeyguardDisplayManager = keyguardDisplayManager; dumpManager.registerDumpable(getClass().getName(), this); mDeviceConfig = deviceConfig; mShowHomeOverLockscreen = mDeviceConfig.getBoolean( @@ -775,9 +773,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { mContext.registerReceiver(mDelayedLockBroadcastReceiver, delayedActionFilter, SYSTEMUI_PERMISSION, null /* scheduler */); - mKeyguardDisplayManager = new KeyguardDisplayManager(mContext, - mInjectionInflationController); - mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); KeyguardUpdateMonitor.setCurrentUser(ActivityManager.getCurrentUser()); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index c9164f0c4459..9d8e73a0ff47 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -26,8 +26,10 @@ import android.os.Handler; import android.os.PowerManager; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardDisplayManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardViewController; +import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -43,7 +45,6 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.KeyguardLiftController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.DeviceConfigProxy; -import com.android.systemui.util.InjectionInflationController; import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.settings.SystemSettings; @@ -58,7 +59,7 @@ import dagger.Provides; /** * Dagger Module providing {@link StatusBar}. */ -@Module +@Module(subcomponents = {KeyguardStatusViewComponent.class}) public class KeyguardModule { /** * Provides our instance of KeyguardViewMediator which is considered optional. @@ -79,7 +80,7 @@ public class KeyguardModule { @UiBackground Executor uiBgExecutor, DeviceConfigProxy deviceConfig, NavigationModeController navigationModeController, - InjectionInflationController injectionInflationController) { + KeyguardDisplayManager keyguardDisplayManager) { return new KeyguardViewMediator( context, falsingManager, @@ -94,7 +95,8 @@ public class KeyguardModule { trustManager, deviceConfig, navigationModeController, - injectionInflationController); + keyguardDisplayManager + ); } @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index a003d8365810..e5a9ac10389f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -172,7 +172,6 @@ class MediaCarouselController @Inject constructor( // This view is inactive, let's remove this! This happens e.g when dismissing / // timing out a view. We still have the data around because resumption could // be on, but we should save the resources and release this. - oldKey?.let { MediaPlayerData.removeMediaPlayer(it) } onMediaDataRemoved(key) } else { addOrUpdatePlayer(key, oldKey, data) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt index 77cac5023db3..486399979db7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt @@ -30,6 +30,7 @@ import com.android.settingslib.Utils import com.android.systemui.Gefingerpoken import com.android.systemui.qs.PageIndicator import com.android.systemui.R +import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS import com.android.systemui.plugins.FalsingManager import com.android.systemui.util.animation.PhysicsAnimator import com.android.systemui.util.concurrency.DelayableExecutor @@ -315,7 +316,8 @@ class MediaCarouselScrollHandler( return false } - private fun isFalseTouch() = falsingProtectionNeeded && falsingManager.isFalseTouch + private fun isFalseTouch() = falsingProtectionNeeded && + falsingManager.isFalseTouch(NOTIFICATION_DISMISS) private fun getMaxTranslation() = if (showsSettingsButton) { settingsButton.width diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index aec3543de4eb..c7e78174f474 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -662,7 +662,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_SLIPPERY, PixelFormat.TRANSLUCENT); mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId()); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java index 0354c727c92c..8ef9b092bc00 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java @@ -158,7 +158,9 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( mDisplaySize.x, mTutorialAreaHeight, 0, 0, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, - WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); lp.gravity = Gravity.TOP | Gravity.LEFT; lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 7421ec14398b..c956702c9216 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -49,6 +49,9 @@ import android.os.RemoteException; import android.util.Log; import android.util.Size; import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.View; +import android.view.WindowManager; import android.window.TaskOrganizer; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -57,6 +60,7 @@ import android.window.WindowOrganizer; import com.android.internal.os.SomeArgs; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.pip.phone.PipMenuActivityController; import com.android.systemui.pip.phone.PipUpdateThread; import com.android.systemui.stackdivider.SplitScreen; import com.android.wm.shell.R; @@ -95,6 +99,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize private static final int MSG_FINISH_RESIZE = 4; private static final int MSG_RESIZE_USER = 5; + private final Context mContext; private final Handler mMainHandler; private final Handler mUpdateHandler; private final PipBoundsHandler mPipBoundsHandler; @@ -107,6 +112,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize private final Map<IBinder, Configuration> mInitialState = new HashMap<>(); private final Optional<SplitScreen> mSplitScreenOptional; protected final ShellTaskOrganizer mTaskOrganizer; + private SurfaceControlViewHost mPipViewHost; + private SurfaceControl mPipMenuSurface; // These callbacks are called on the update thread private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = @@ -212,6 +219,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize @NonNull DisplayController displayController, @NonNull PipUiEventLogger pipUiEventLogger, @NonNull ShellTaskOrganizer shellTaskOrganizer) { + mContext = context; mMainHandler = new Handler(Looper.getMainLooper()); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mPipBoundsHandler = boundsHandler; @@ -504,6 +512,45 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize } /** + * Setup the ViewHost and attach the provided menu view to the ViewHost. + */ + public void attachPipMenuViewHost(View menuView, WindowManager.LayoutParams lp) { + if (mPipMenuSurface != null) { + Log.e(TAG, "PIP Menu View already created and attached."); + return; + } + + if (Looper.getMainLooper() != Looper.myLooper()) { + throw new RuntimeException("PipMenuView needs to be attached on the main thread."); + } + + mPipViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), + (android.os.Binder) null); + mPipMenuSurface = mPipViewHost.getSurfacePackage().getSurfaceControl(); + SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); + transaction.reparent(mPipMenuSurface, mLeash); + transaction.show(mPipMenuSurface); + transaction.setRelativeLayer(mPipMenuSurface, mLeash, 1); + transaction.apply(); + mPipViewHost.setView(menuView, lp); + } + + + /** + * Releases the PIP Menu's View host, remove it from PIP task surface. + */ + public void detachPipMenuViewHost() { + if (mPipMenuSurface != null) { + SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); + transaction.remove(mPipMenuSurface); + transaction.apply(); + mPipMenuSurface = null; + mPipViewHost = null; + } + } + + + /** * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}. * Meanwhile this callback is invoked whenever the task is removed. For instance: * - as a result of removeStacksInWindowingModes from WM @@ -838,6 +885,12 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize WindowContainerTransaction wct = new WindowContainerTransaction(); prepareFinishResizeTransaction(destinationBounds, direction, tx, wct); applyFinishBoundsResize(wct, direction); + runOnMainHandler(() -> { + if (mPipViewHost != null) { + mPipViewHost.relayout(PipMenuActivityController.getPipMenuLayoutParams( + destinationBounds.width(), destinationBounds.height())); + } + }); } private void prepareFinishResizeTransaction(Rect destinationBounds, diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index d8864ec72dc3..5ef5b902327e 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -49,7 +49,6 @@ import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSurfaceTransactionHelper; import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.pip.PipUiEventLogger; -import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputConsumerController; @@ -267,7 +266,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Inject public PipManager(Context context, BroadcastDispatcher broadcastDispatcher, - @PipMenuActivityClass Class<?> pipMenuActivityClass, ConfigurationController configController, DeviceConfigProxy deviceConfig, DisplayController displayController, @@ -296,8 +294,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mPipTaskOrganizer.registerPipTransitionCallback(this); mInputConsumerController = InputConsumerController.getPipInputConsumer(); mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher); - mMenuController = new PipMenuActivityController(context, pipMenuActivityClass, - mMediaController, mInputConsumerController); + mMenuController = new PipMenuActivityController(context, + mMediaController, mInputConsumerController, mPipTaskOrganizer); mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, floatingContentCoordinator, deviceConfig, sysUiState, pipUiEventLogger); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index 383f6b3bf79d..873ba26b39ab 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -19,27 +19,20 @@ package com.android.systemui.pip.phone; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import android.annotation.Nullable; import android.app.ActivityManager.StackInfo; -import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.RemoteAction; import android.content.Context; -import android.content.Intent; import android.content.pm.ParceledListSlice; +import android.graphics.PixelFormat; import android.graphics.Rect; -import android.os.Bundle; import android.os.Debug; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; import android.util.Log; import android.view.MotionEvent; +import android.view.WindowManager; +import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.pip.phone.PipMediaController.ActionListener; import com.android.systemui.shared.system.InputConsumerController; @@ -58,30 +51,10 @@ public class PipMenuActivityController { private static final String TAG = "PipMenuActController"; private static final boolean DEBUG = false; - public static final String EXTRA_CONTROLLER_MESSENGER = "messenger"; - public static final String EXTRA_ACTIONS = "actions"; - public static final String EXTRA_STACK_BOUNDS = "stack_bounds"; - public static final String EXTRA_ALLOW_TIMEOUT = "allow_timeout"; - public static final String EXTRA_WILL_RESIZE_MENU = "resize_menu_on_show"; - public static final String EXTRA_DISMISS_FRACTION = "dismiss_fraction"; - public static final String EXTRA_MENU_STATE = "menu_state"; - public static final String EXTRA_SHOW_MENU_WITH_DELAY = "show_menu_with_delay"; - public static final String EXTRA_SHOW_RESIZE_HANDLE = "show_resize_handle"; - public static final String EXTRA_MESSAGE_CALLBACK_WHAT = "message_callback_what"; - - public static final int MESSAGE_MENU_STATE_CHANGED = 100; - public static final int MESSAGE_EXPAND_PIP = 101; - public static final int MESSAGE_DISMISS_PIP = 103; - public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104; - public static final int MESSAGE_SHOW_MENU = 107; - public static final int MENU_STATE_NONE = 0; public static final int MENU_STATE_CLOSE = 1; public static final int MENU_STATE_FULL = 2; - // The duration to wait before we consider the start activity as having timed out - private static final long START_ACTIVITY_REQUEST_TIMEOUT_MS = 300; - /** * A listener interface to receive notification on changes in PIP. */ @@ -110,9 +83,8 @@ public class PipMenuActivityController { void onPipShowMenu(); } - /** TODO(b/150319024): PipMenuActivity will move to a Window */ - private Class<?> mPipMenuActivityClass; private Context mContext; + private PipTaskOrganizer mPipTaskOrganizer; private PipMediaController mMediaController; private InputConsumerController mInputConsumerController; @@ -121,63 +93,7 @@ public class PipMenuActivityController { private ParceledListSlice<RemoteAction> mMediaActions; private int mMenuState; - // The dismiss fraction update is sent frequently, so use a temporary bundle for the message - private Bundle mTmpDismissFractionData = new Bundle(); - - private Runnable mOnAnimationEndRunnable; - private boolean mStartActivityRequested; - private long mStartActivityRequestedTime; - private Messenger mToActivityMessenger; - private Handler mHandler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_MENU_STATE_CHANGED: { - final int menuState = msg.arg1; - final boolean resize = msg.arg2 != 0; - onMenuStateChanged(menuState, resize, - getMenuStateChangeFinishedCallback(msg.replyTo, (Bundle) msg.obj)); - break; - } - case MESSAGE_EXPAND_PIP: { - mListeners.forEach(Listener::onPipExpand); - break; - } - case MESSAGE_DISMISS_PIP: { - mListeners.forEach(Listener::onPipDismiss); - break; - } - case MESSAGE_SHOW_MENU: { - mListeners.forEach(Listener::onPipShowMenu); - break; - } - case MESSAGE_UPDATE_ACTIVITY_CALLBACK: { - mToActivityMessenger = msg.replyTo; - setStartActivityRequested(false); - if (mOnAnimationEndRunnable != null) { - mOnAnimationEndRunnable.run(); - mOnAnimationEndRunnable = null; - } - // Mark the menu as invisible once the activity finishes as well - if (mToActivityMessenger == null) { - final boolean resize = msg.arg1 != 0; - onMenuStateChanged(MENU_STATE_NONE, resize, null /* callback */); - } - break; - } - } - } - }; - private Messenger mMessenger = new Messenger(mHandler); - - private Runnable mStartActivityRequestedTimeoutRunnable = () -> { - setStartActivityRequested(false); - if (mOnAnimationEndRunnable != null) { - mOnAnimationEndRunnable.run(); - mOnAnimationEndRunnable = null; - } - Log.e(TAG, "Expected start menu activity request timed out"); - }; + private PipMenuView mPipMenuView; private ActionListener mMediaActionListener = new ActionListener() { @Override @@ -187,39 +103,40 @@ public class PipMenuActivityController { } }; - public PipMenuActivityController(Context context, Class<?> pipMenuActivityClass, - PipMediaController mediaController, InputConsumerController inputConsumerController + public PipMenuActivityController(Context context, + PipMediaController mediaController, InputConsumerController inputConsumerController, + PipTaskOrganizer pipTaskOrganizer ) { mContext = context; mMediaController = mediaController; mInputConsumerController = inputConsumerController; - mPipMenuActivityClass = pipMenuActivityClass; + mPipTaskOrganizer = pipTaskOrganizer; } - public boolean isMenuActivityVisible() { - return mToActivityMessenger != null; + public boolean isMenuVisible() { + return mPipMenuView != null && mMenuState != MENU_STATE_NONE; } public void onActivityPinned() { + if (mPipMenuView == null) { + WindowManager.LayoutParams lp = + getPipMenuLayoutParams(0, 0); + mPipMenuView = new PipMenuView(mContext, this); + mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView, lp); + } mInputConsumerController.registerInputConsumer(true /* withSfVsync */); } public void onActivityUnpinned() { hideMenu(); mInputConsumerController.unregisterInputConsumer(); - setStartActivityRequested(false); + mPipTaskOrganizer.detachPipMenuViewHost(); + mPipMenuView = null; } public void onPinnedStackAnimationEnded() { - // Note: Only active menu activities care about this event - if (mToActivityMessenger != null) { - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_ANIMATION_ENDED; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify menu pinned animation ended", e); - } + if (isMenuVisible()) { + mPipMenuView.onPipAnimationEnded(); } } @@ -236,27 +153,13 @@ public class PipMenuActivityController { * Updates the appearance of the menu and scrim on top of the PiP while dismissing. */ public void setDismissFraction(float fraction) { + final boolean isMenuVisible = isMenuVisible(); if (DEBUG) { - Log.d(TAG, "setDismissFraction() hasActivity=" + (mToActivityMessenger != null) + Log.d(TAG, "setDismissFraction() isMenuVisible=" + isMenuVisible + " fraction=" + fraction); } - if (mToActivityMessenger != null) { - mTmpDismissFractionData.clear(); - mTmpDismissFractionData.putFloat(EXTRA_DISMISS_FRACTION, fraction); - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_UPDATE_DISMISS_FRACTION; - m.obj = mTmpDismissFractionData; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify menu to update dismiss fraction", e); - } - } else if (!mStartActivityRequested || isStartActivityRequestedElapsed()) { - // If we haven't requested the start activity, or if it previously took too long to - // start, then start it - startMenuActivity(MENU_STATE_NONE, null /* stackBounds */, - false /* allowMenuTimeout */, false /* resizeMenuOnShow */, - false /* withDelay */, false /* showResizeHandle */); + if (isMenuVisible) { + mPipMenuView.updateDismissFraction(fraction); } } @@ -282,27 +185,11 @@ public class PipMenuActivityController { false /* withDelay */, showResizeHandle); } - private Runnable getMenuStateChangeFinishedCallback(@Nullable Messenger replyTo, - @Nullable Bundle data) { - if (replyTo == null || data == null) { - return null; - } - return () -> { - try { - final Message m = Message.obtain(); - m.what = data.getInt(EXTRA_MESSAGE_CALLBACK_WHAT); - replyTo.send(m); - } catch (RemoteException e) { - // ignored - } - }; - } - private void showMenuInternal(int menuState, Rect stackBounds, boolean allowMenuTimeout, boolean willResizeMenu, boolean withDelay, boolean showResizeHandle) { if (DEBUG) { Log.d(TAG, "showMenu() state=" + menuState - + " hasActivity=" + (mToActivityMessenger != null) + + " isMenuVisible=" + isMenuVisible() + " allowMenuTimeout=" + allowMenuTimeout + " willResizeMenu=" + willResizeMenu + " withDelay=" + withDelay @@ -310,64 +197,34 @@ public class PipMenuActivityController { + " callers=\n" + Debug.getCallers(5, " ")); } - if (mToActivityMessenger != null) { - Bundle data = new Bundle(); - data.putInt(EXTRA_MENU_STATE, menuState); - if (stackBounds != null) { - data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds); - } - data.putBoolean(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout); - data.putBoolean(EXTRA_WILL_RESIZE_MENU, willResizeMenu); - data.putBoolean(EXTRA_SHOW_MENU_WITH_DELAY, withDelay); - data.putBoolean(EXTRA_SHOW_RESIZE_HANDLE, showResizeHandle); - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_SHOW_MENU; - m.obj = data; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify menu to show", e); - } - } else if (!mStartActivityRequested || isStartActivityRequestedElapsed()) { - // If we haven't requested the start activity, or if it previously took too long to - // start, then start it - startMenuActivity(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay, - showResizeHandle); + if (mPipMenuView == null) { + Log.e(TAG, "PipMenu has not been attached yet."); + return; } + mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay, + showResizeHandle); } /** * Pokes the menu, indicating that the user is interacting with it. */ public void pokeMenu() { + final boolean isMenuVisible = isMenuVisible(); if (DEBUG) { - Log.d(TAG, "pokeMenu() hasActivity=" + (mToActivityMessenger != null)); + Log.d(TAG, "pokeMenu() isMenuVisible=" + isMenuVisible); } - if (mToActivityMessenger != null) { - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_POKE_MENU; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify poke menu", e); - } + if (isMenuVisible) { + mPipMenuView.pokeMenu(); } } private void fadeOutMenu() { + final boolean isMenuVisible = isMenuVisible(); if (DEBUG) { - Log.d(TAG, "fadeOutMenu() state=" + mMenuState - + " hasActivity=" + (mToActivityMessenger != null) - + " callers=\n" + Debug.getCallers(5, " ")); + Log.d(TAG, "fadeOutMenu() isMenuVisible=" + isMenuVisible); } - if (mToActivityMessenger != null) { - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_FADE_OUT_MENU; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify menu to fade out", e); - } + if (isMenuVisible) { + mPipMenuView.fadeOutMenu(); } } @@ -375,19 +232,14 @@ public class PipMenuActivityController { * Hides the menu activity. */ public void hideMenu() { + final boolean isMenuVisible = isMenuVisible(); if (DEBUG) { Log.d(TAG, "hideMenu() state=" + mMenuState - + " hasActivity=" + (mToActivityMessenger != null) + + " isMenuVisible=" + isMenuVisible + " callers=\n" + Debug.getCallers(5, " ")); } - if (mToActivityMessenger != null) { - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_HIDE_MENU; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify menu to hide", e); - } + if (isMenuVisible) { + mPipMenuView.hideMenu(); } } @@ -395,29 +247,11 @@ public class PipMenuActivityController { * Hides the menu activity. */ public void hideMenu(Runnable onStartCallback, Runnable onEndCallback) { - if (mStartActivityRequested) { - // If the menu has been start-requested, but not actually started, then we defer the - // trigger callback until the menu has started and called back to the controller. - mOnAnimationEndRunnable = onEndCallback; - onStartCallback.run(); - - // Fallback for b/63752800, we have started the PipMenuActivity but it has not made any - // callbacks. Don't continue to wait for the menu to show past some timeout. - mHandler.removeCallbacks(mStartActivityRequestedTimeoutRunnable); - mHandler.postDelayed(mStartActivityRequestedTimeoutRunnable, - START_ACTIVITY_REQUEST_TIMEOUT_MS); - } else if (mMenuState != MENU_STATE_NONE && mToActivityMessenger != null) { + if (isMenuVisible()) { // If the menu is visible in either the closed or full state, then hide the menu and // trigger the animation trigger afterwards onStartCallback.run(); - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_HIDE_MENU; - m.obj = onEndCallback; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify hide menu", e); - } + mPipMenuView.hideMenu(onEndCallback); } } @@ -438,6 +272,18 @@ public class PipMenuActivityController { updateMenuActions(); } + void onPipExpand() { + mListeners.forEach(Listener::onPipExpand); + } + + void onPipDismiss() { + mListeners.forEach(Listener::onPipDismiss); + } + + void onPipShowMenu() { + mListeners.forEach(Listener::onPipShowMenu); + } + /** * @return the best set of actions to show in the PiP menu. */ @@ -449,47 +295,20 @@ public class PipMenuActivityController { } /** - * Starts the menu activity on the top task of the pinned stack. + * Returns a default LayoutParams for the PIP Menu. + * @param width the PIP stack width. + * @param height the PIP stack height. */ - private void startMenuActivity(int menuState, Rect stackBounds, boolean allowMenuTimeout, - boolean willResizeMenu, boolean withDelay, boolean showResizeHandle) { - try { - StackInfo pinnedStackInfo = ActivityTaskManager.getService().getStackInfo( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); - if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null && - pinnedStackInfo.taskIds.length > 0) { - Intent intent = new Intent(mContext, mPipMenuActivityClass); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger); - intent.putExtra(EXTRA_ACTIONS, resolveMenuActions()); - if (stackBounds != null) { - intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds); - } - intent.putExtra(EXTRA_MENU_STATE, menuState); - intent.putExtra(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout); - intent.putExtra(EXTRA_WILL_RESIZE_MENU, willResizeMenu); - intent.putExtra(EXTRA_SHOW_MENU_WITH_DELAY, withDelay); - intent.putExtra(EXTRA_SHOW_RESIZE_HANDLE, showResizeHandle); - ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0); - options.setLaunchTaskId( - pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]); - options.setTaskOverlay(true, true /* canResume */); - mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT); - setStartActivityRequested(true); - } else { - Log.e(TAG, "No PIP tasks found"); - } - } catch (RemoteException e) { - setStartActivityRequested(false); - Log.e(TAG, "Error showing PIP menu activity", e); - } + public static WindowManager.LayoutParams getPipMenuLayoutParams(int width, int height) { + return new WindowManager.LayoutParams(width, height, + WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSLUCENT); } /** - * Updates the PiP menu activity with the best set of actions provided. + * Updates the PiP menu with the best set of actions provided. */ private void updateMenuActions() { - if (mToActivityMessenger != null) { + if (isMenuVisible()) { // Fetch the pinned stack bounds Rect stackBounds = null; try { @@ -499,20 +318,10 @@ public class PipMenuActivityController { stackBounds = pinnedStackInfo.bounds; } } catch (RemoteException e) { - Log.e(TAG, "Error showing PIP menu activity", e); + Log.e(TAG, "Error showing PIP menu", e); } - Bundle data = new Bundle(); - data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds); - data.putParcelable(EXTRA_ACTIONS, resolveMenuActions()); - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_UPDATE_ACTIONS; - m.obj = data; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not notify menu activity to update actions", e); - } + mPipMenuView.setActions(stackBounds, resolveMenuActions().getList()); } } @@ -524,17 +333,9 @@ public class PipMenuActivityController { } /** - * @return whether the time of the activity request has exceeded the timeout. - */ - private boolean isStartActivityRequestedElapsed() { - return (SystemClock.uptimeMillis() - mStartActivityRequestedTime) - >= START_ACTIVITY_REQUEST_TIMEOUT_MS; - } - - /** * Handles changes in menu visibility. */ - private void onMenuStateChanged(int menuState, boolean resize, Runnable callback) { + void onMenuStateChanged(int menuState, boolean resize, Runnable callback) { if (DEBUG) { Log.d(TAG, "onMenuStateChanged() mMenuState=" + mMenuState + " menuState=" + menuState + " resize=" + resize @@ -556,25 +357,14 @@ public class PipMenuActivityController { mMenuState = menuState; } - private void setStartActivityRequested(boolean requested) { - mHandler.removeCallbacks(mStartActivityRequestedTimeoutRunnable); - mStartActivityRequested = requested; - mStartActivityRequestedTime = requested ? SystemClock.uptimeMillis() : 0; - } - /** * Handles a pointer event sent from pip input consumer. */ void handlePointerEvent(MotionEvent ev) { - if (mToActivityMessenger != null) { - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_POINTER_EVENT; - m.obj = ev; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not dispatch touch event", e); - } + if (ev.isTouchEvent()) { + mPipMenuView.dispatchTouchEvent(ev); + } else { + mPipMenuView.dispatchGenericMotionEvent(ev); } } @@ -582,15 +372,14 @@ public class PipMenuActivityController { * Tell the PIP Menu to recalculate its layout given its current position on the display. */ public void updateMenuLayout(Rect bounds) { - if (mToActivityMessenger != null) { - Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_UPDATE_MENU_LAYOUT; - m.obj = bounds; - try { - mToActivityMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Could not dispatch touch event", e); - } + final boolean isMenuVisible = isMenuVisible(); + if (DEBUG) { + Log.d(TAG, "updateMenuLayout() state=" + mMenuState + + " isMenuVisible=" + isMenuVisible + + " callers=\n" + Debug.getCallers(5, " ")); + } + if (isMenuVisible) { + mPipMenuView.updateMenuLayout(bounds); } } @@ -598,9 +387,7 @@ public class PipMenuActivityController { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); pw.println(innerPrefix + "mMenuState=" + mMenuState); - pw.println(innerPrefix + "mToActivityMessenger=" + mToActivityMessenger); + pw.println(innerPrefix + "mPipMenuView=" + mPipMenuView); pw.println(innerPrefix + "mListeners=" + mListeners.size()); - pw.println(innerPrefix + "mStartActivityRequested=" + mStartActivityRequested); - pw.println(innerPrefix + "mStartActivityRequestedTime=" + mStartActivityRequestedTime); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java index 1b1b2de05883..993bfe04f9a3 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 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. @@ -11,7 +11,7 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ package com.android.systemui.pip.phone; @@ -23,15 +23,6 @@ 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.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ALLOW_TIMEOUT; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_DISMISS_FRACTION; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_MENU_STATE; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_MENU_WITH_DELAY; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_RESIZE_HANDLE; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_STACK_BOUNDS; -import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_WILL_RESIZE_MENU; import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE; import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL; import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE; @@ -41,14 +32,12 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; -import android.annotation.Nullable; -import android.app.Activity; import android.app.ActivityManager; import android.app.PendingIntent.CanceledException; import android.app.RemoteAction; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; -import android.content.pm.ParceledListSlice; import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; @@ -56,10 +45,6 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.util.Pair; @@ -68,7 +53,6 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; @@ -76,32 +60,20 @@ import android.widget.ImageButton; import android.widget.LinearLayout; import com.android.systemui.Interpolators; -import com.android.wm.shell.R; +import com.android.systemui.R; import java.util.ArrayList; -import java.util.Collections; import java.util.List; /** - * Translucent activity that gets started on top of a task in PIP to allow the user to control it. - * TODO(b/150319024): PipMenuActivity will move to a Window + * Translucent window that gets started on top of a task in PIP to allow the user to control it. */ -public class PipMenuActivity extends Activity { +public class PipMenuView extends FrameLayout { - private static final String TAG = "PipMenuActivity"; + private static final String TAG = "PipMenuView"; private static final int MESSAGE_INVALID_TYPE = -1; - - public static final int MESSAGE_SHOW_MENU = 1; - public static final int MESSAGE_POKE_MENU = 2; - public static final int MESSAGE_HIDE_MENU = 3; - public static final int MESSAGE_UPDATE_ACTIONS = 4; - public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5; - public static final int MESSAGE_ANIMATION_ENDED = 6; - public static final int MESSAGE_POINTER_EVENT = 7; public static final int MESSAGE_MENU_EXPANDED = 8; - public static final int MESSAGE_FADE_OUT_MENU = 9; - public static final int MESSAGE_UPDATE_MENU_LAYOUT = 10; private static final int INITIAL_DISMISS_DELAY = 3500; private static final int POST_INTERACTION_DISMISS_DELAY = 2000; @@ -130,85 +102,20 @@ public class PipMenuActivity extends Activity { private int mBetweenActionPaddingLand; private AnimatorSet mMenuContainerAnimator; + private PipMenuActivityController mController; private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener = new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { final float alpha = (float) animation.getAnimatedValue(); - mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255)); + mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA * alpha * 255)); } }; - private Handler mHandler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_SHOW_MENU: { - final Bundle data = (Bundle) msg.obj; - showMenu(data.getInt(EXTRA_MENU_STATE), - data.getParcelable(EXTRA_STACK_BOUNDS), - data.getBoolean(EXTRA_ALLOW_TIMEOUT), - data.getBoolean(EXTRA_WILL_RESIZE_MENU), - data.getBoolean(EXTRA_SHOW_MENU_WITH_DELAY), - data.getBoolean(EXTRA_SHOW_RESIZE_HANDLE)); - break; - } - case MESSAGE_POKE_MENU: - cancelDelayedFinish(); - break; - case MESSAGE_HIDE_MENU: - hideMenu((Runnable) msg.obj); - break; - case MESSAGE_UPDATE_ACTIONS: { - final Bundle data = (Bundle) msg.obj; - final ParceledListSlice<RemoteAction> actions = data.getParcelable( - EXTRA_ACTIONS); - setActions(data.getParcelable(EXTRA_STACK_BOUNDS), actions != null - ? actions.getList() : Collections.emptyList()); - break; - } - case MESSAGE_UPDATE_DISMISS_FRACTION: { - final Bundle data = (Bundle) msg.obj; - updateDismissFraction(data.getFloat(EXTRA_DISMISS_FRACTION)); - break; - } - case MESSAGE_ANIMATION_ENDED: { - mAllowTouches = true; - break; - } - case MESSAGE_POINTER_EVENT: { - final MotionEvent ev = (MotionEvent) msg.obj; - dispatchPointerEvent(ev); - break; - } - case MESSAGE_MENU_EXPANDED : { - if (mMenuContainerAnimator == null) { - return; - } - mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY); - mMenuContainerAnimator.start(); - break; - } - case MESSAGE_FADE_OUT_MENU: { - fadeOutMenu(); - break; - } - case MESSAGE_UPDATE_MENU_LAYOUT: { - if (mPipMenuIconsAlgorithm == null) { - return; - } - final Rect bounds = (Rect) msg.obj; - mPipMenuIconsAlgorithm.onBoundsChanged(bounds); - break; - } - } - } - }; - private Messenger mToControllerMessenger; - private Messenger mMessenger = new Messenger(mHandler); + private Handler mHandler = new Handler(); - private final Runnable mFinishRunnable = this::hideMenu; + private final Runnable mHideMenuRunnable = this::hideMenu; protected View mViewRoot; protected View mSettingsButton; @@ -217,17 +124,14 @@ public class PipMenuActivity extends Activity { protected View mTopEndContainer; protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm; - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - // Set the flags to allow us to watch for outside touches and also hide the menu and start - // manipulating the PIP in the same touch gesture - getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); - - super.onCreate(savedInstanceState); + public PipMenuView(Context context, PipMenuActivityController controller) { + super(context, null, 0); + mContext = context; + mController = controller; - setContentView(R.layout.pip_menu_activity); + mAccessibilityManager = context.getSystemService(AccessibilityManager.class); + inflate(context, R.layout.pip_menu, this); - mAccessibilityManager = getSystemService(AccessibilityManager.class); mBackgroundDrawable = new ColorDrawable(Color.BLACK); mBackgroundDrawable.setAlpha(0); mViewRoot = findViewById(R.id.background); @@ -250,26 +154,25 @@ public class PipMenuActivity extends Activity { expandPip(); } }); + // TODO (b/161710689): Remove this once focusability for Windowless window is working + findViewById(R.id.expand_button).setFocusable(false); + mDismissButton.setFocusable(false); + mSettingsButton.setFocusable(false); + mResizeHandle = findViewById(R.id.resize_handle); mResizeHandle.setAlpha(0); mActionsGroup = findViewById(R.id.actions_group); mBetweenActionPaddingLand = getResources().getDimensionPixelSize( R.dimen.pip_between_action_padding_land); - mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(this.getApplicationContext()); + mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext); mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer, mResizeHandle, mSettingsButton, mDismissButton); - updateFromIntent(getIntent()); - setTitle(R.string.pip_menu_title); - setDisablePreviewScreenshots(true); - - // Hide without an animation. - getWindow().setExitTransition(null); initAccessibility(); } private void initAccessibility() { - getWindow().getDecorView().setAccessibilityDelegate(new View.AccessibilityDelegate() { + this.setAccessibilityDelegate(new View.AccessibilityDelegate() { @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); @@ -280,9 +183,7 @@ public class PipMenuActivity extends Activity { @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) { - Message m = Message.obtain(); - m.what = PipMenuActivityController.MESSAGE_SHOW_MENU; - sendMessage(m, "Could not notify controller to show PIP menu"); + mController.onPipShowMenu(); } return super.performAccessibilityAction(host, action, args); } @@ -299,108 +200,37 @@ public class PipMenuActivity extends Activity { } @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - updateFromIntent(intent); - } - - @Override - public void onUserInteraction() { - if (mAllowMenuTimeout) { - repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY); - } - } - - @Override - protected void onUserLeaveHint() { - super.onUserLeaveHint(); - - // If another task is starting on top of the menu, then hide and finish it so that it can be - // recreated on the top next time it starts - hideMenu(); - } - - @Override - public void onTopResumedActivityChanged(boolean isTopResumedActivity) { - super.onTopResumedActivityChanged(isTopResumedActivity); - if (!isTopResumedActivity && mMenuState != MENU_STATE_NONE) { - hideMenu(); - } - } - - @Override - protected void onStop() { - super.onStop(); - - // In cases such as device lock, hide and finish it so that it can be recreated on the top - // next time it starts, see also {@link #onUserLeaveHint} - hideMenu(); - cancelDelayedFinish(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - - // Fallback, if we are destroyed for any other reason (like when the task is being reset), - // also reset the callback. - notifyActivityCallback(null); - } - - @Override - public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { - if (!isInPictureInPictureMode) { - finish(); - } - } - - /** - * Dispatch a pointer event from {@link PipTouchHandler}. - */ - private void dispatchPointerEvent(MotionEvent event) { - if (event.isTouchEvent()) { - dispatchTouchEvent(event); - } else { - dispatchGenericMotionEvent(event); - } - } - - @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (!mAllowTouches) { return false; } - // On the first action outside the window, hide the menu - switch (ev.getAction()) { - case MotionEvent.ACTION_OUTSIDE: - hideMenu(); - return true; + if (mAllowMenuTimeout) { + repostDelayedHide(POST_INTERACTION_DISMISS_DELAY); } + return super.dispatchTouchEvent(ev); } @Override - public void finish() { - notifyActivityCallback(null); - super.finish(); - } + public boolean dispatchGenericMotionEvent(MotionEvent event) { + if (mAllowMenuTimeout) { + repostDelayedHide(POST_INTERACTION_DISMISS_DELAY); + } - @Override - public void setTaskDescription(ActivityManager.TaskDescription taskDescription) { - // Do nothing + return super.dispatchGenericMotionEvent(event); } - private void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout, + void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout, boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) { mAllowMenuTimeout = allowMenuTimeout; if (mMenuState != menuState) { // Disallow touches if the menu needs to resize while showing, and we are transitioning // to/from a full menu state. - boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow && - (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL); + boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow + && (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL); mAllowTouches = !disallowTouchesUntilAnimationEnd; - cancelDelayedFinish(); + cancelDelayedHide(); updateActionViews(stackBounds); if (mMenuContainerAnimator != null) { mMenuContainerAnimator.cancel(); @@ -431,22 +261,28 @@ public class PipMenuActivity extends Activity { mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - repostDelayedFinish(INITIAL_DISMISS_DELAY); + repostDelayedHide(INITIAL_DISMISS_DELAY); } }); } if (withDelay) { // starts the menu container animation after window expansion is completed - notifyMenuStateChange(menuState, resizeMenuOnShow, MESSAGE_MENU_EXPANDED); + notifyMenuStateChange(menuState, resizeMenuOnShow, () -> { + if (mMenuContainerAnimator == null) { + return; + } + mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY); + mMenuContainerAnimator.start(); + }); } else { - notifyMenuStateChange(menuState, resizeMenuOnShow, MESSAGE_INVALID_TYPE); + notifyMenuStateChange(menuState, resizeMenuOnShow, null); mMenuContainerAnimator.start(); } } else { // If we are already visible, then just start the delayed dismiss and unregister any // existing input consumers from the previous drag if (allowMenuTimeout) { - repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY); + repostDelayedHide(POST_INTERACTION_DISMISS_DELAY); } } } @@ -456,28 +292,39 @@ public class PipMenuActivity extends Activity { * and instead, it fades out the controls by setting the alpha to 0 directly without menu * visibility callbacks invoked. */ - private void fadeOutMenu() { + void fadeOutMenu() { mMenuContainer.setAlpha(0f); mSettingsButton.setAlpha(0f); mDismissButton.setAlpha(0f); mResizeHandle.setAlpha(0f); } - private void hideMenu() { + void pokeMenu() { + cancelDelayedHide(); + } + + void onPipAnimationEnded() { + mAllowTouches = true; + } + + void updateMenuLayout(Rect bounds) { + mPipMenuIconsAlgorithm.onBoundsChanged(bounds); + } + + void hideMenu() { hideMenu(null); } - private void hideMenu(Runnable animationEndCallback) { - hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false /* isDismissing */, - true /* animate */); + void hideMenu(Runnable animationEndCallback) { + hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false); } private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility, - boolean isDismissing, boolean animate) { + boolean animate) { if (mMenuState != MENU_STATE_NONE) { - cancelDelayedFinish(); + cancelDelayedHide(); if (notifyMenuVisibility) { - notifyMenuStateChange(MENU_STATE_NONE, mResize, MESSAGE_INVALID_TYPE); + notifyMenuStateChange(MENU_STATE_NONE, mResize, null); } mMenuContainerAnimator = new AnimatorSet(); ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA, @@ -498,49 +345,13 @@ public class PipMenuActivity extends Activity { if (animationFinishedRunnable != null) { animationFinishedRunnable.run(); } - - if (!isDismissing) { - // If we are dismissing the PiP, then don't try to pre-emptively finish the - // menu activity - finish(); - } } }); mMenuContainerAnimator.start(); - } else { - // If the menu is not visible, just finish now - finish(); } } - private void updateFromIntent(Intent intent) { - mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER); - if (mToControllerMessenger == null) { - Log.w(TAG, "Controller messenger is null. Stopping."); - finish(); - return; - } - notifyActivityCallback(mMessenger); - - ParceledListSlice<RemoteAction> actions = intent.getParcelableExtra(EXTRA_ACTIONS); - if (actions != null) { - mActions.clear(); - mActions.addAll(actions.getList()); - } - - final int menuState = intent.getIntExtra(EXTRA_MENU_STATE, MENU_STATE_NONE); - if (menuState != MENU_STATE_NONE) { - Rect stackBounds = intent.getParcelableExtra(EXTRA_STACK_BOUNDS); - boolean allowMenuTimeout = intent.getBooleanExtra(EXTRA_ALLOW_TIMEOUT, true); - boolean willResizeMenu = intent.getBooleanExtra(EXTRA_WILL_RESIZE_MENU, false); - boolean withDelay = intent.getBooleanExtra(EXTRA_SHOW_MENU_WITH_DELAY, false); - boolean showResizeHandle = intent.getBooleanExtra(EXTRA_SHOW_RESIZE_HANDLE, false); - showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay, - showResizeHandle); - } - } - - private void setActions(Rect stackBounds, List<RemoteAction> actions) { + void setActions(Rect stackBounds, List<RemoteAction> actions) { mActions.clear(); mActions.addAll(actions); updateActionViews(stackBounds); @@ -560,7 +371,7 @@ public class PipMenuActivity extends Activity { actionsContainer.setVisibility(View.VISIBLE); if (mActionsGroup != null) { // Ensure we have as many buttons as actions - final LayoutInflater inflater = LayoutInflater.from(this); + final LayoutInflater inflater = LayoutInflater.from(mContext); while (mActionsGroup.getChildCount() < mActions.size()) { final ImageButton actionView = (ImageButton) inflater.inflate( R.layout.pip_menu_action, mActionsGroup, false); @@ -575,14 +386,14 @@ public class PipMenuActivity extends Activity { } // Recreate the layout - final boolean isLandscapePip = stackBounds != null && - (stackBounds.width() > stackBounds.height()); + final boolean isLandscapePip = stackBounds != null + && (stackBounds.width() > stackBounds.height()); for (int i = 0; i < mActions.size(); i++) { final RemoteAction action = mActions.get(i); final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i); // TODO: Check if the action drawable has changed before we reload it - action.getIcon().loadDrawableAsync(this, d -> { + action.getIcon().loadDrawableAsync(mContext, d -> { d.setTint(Color.WHITE); actionView.setImageDrawable(d); }, mHandler); @@ -620,7 +431,7 @@ public class PipMenuActivity extends Activity { } } - private void updateDismissFraction(float fraction) { + void updateDismissFraction(float fraction) { int alpha; final float menuAlpha = 1 - fraction; if (mMenuState == MENU_STATE_FULL) { @@ -639,31 +450,15 @@ public class PipMenuActivity extends Activity { mBackgroundDrawable.setAlpha(alpha); } - private void notifyMenuStateChange(int menuState, boolean resize, int callbackWhat) { + private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) { mMenuState = menuState; - mResize = resize; - Message m = Message.obtain(); - m.what = PipMenuActivityController.MESSAGE_MENU_STATE_CHANGED; - m.arg1 = menuState; - m.arg2 = resize ? 1 : 0; - if (callbackWhat != MESSAGE_INVALID_TYPE) { - // This message could be sent across processes when in secondary user. - // Make the receiver end sending back via our own Messenger - m.replyTo = mMessenger; - final Bundle data = new Bundle(1); - data.putInt(PipMenuActivityController.EXTRA_MESSAGE_CALLBACK_WHAT, callbackWhat); - m.obj = data; - } - sendMessage(m, "Could not notify controller of PIP menu visibility"); + mController.onMenuStateChanged(menuState, resize, callback); } private void expandPip() { // Do not notify menu visibility when hiding the menu, the controller will do this when it // handles the message - hideMenu(() -> { - sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP, - "Could not notify controller to expand PIP"); - }, false /* notifyMenuVisibility */, false /* isDismissing */, true /* animate */); + hideMenu(mController::onPipExpand, false /* notifyMenuVisibility */, true /* animate */); } private void dismissPip() { @@ -673,58 +468,30 @@ public class PipMenuActivity extends Activity { final boolean animate = mMenuState != MENU_STATE_CLOSE; // Do not notify menu visibility when hiding the menu, the controller will do this when it // handles the message - hideMenu(() -> { - sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP, - "Could not notify controller to dismiss PIP"); - }, false /* notifyMenuVisibility */, true /* isDismissing */, animate); + hideMenu(mController::onPipDismiss, false /* notifyMenuVisibility */, animate); } private void showSettings() { final Pair<ComponentName, Integer> topPipActivityInfo = - PipUtils.getTopPipActivity(this, ActivityManager.getService()); + PipUtils.getTopPipActivity(mContext, ActivityManager.getService()); if (topPipActivityInfo.first != null) { final UserHandle user = UserHandle.of(topPipActivityInfo.second); final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS, Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null)); settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user); settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); - startActivity(settingsIntent); - } - } - - private void notifyActivityCallback(Messenger callback) { - Message m = Message.obtain(); - m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK; - m.replyTo = callback; - m.arg1 = mResize ? 1 : 0; - sendMessage(m, "Could not notify controller of activity finished"); - } - - private void sendEmptyMessage(int what, String errorMsg) { - Message m = Message.obtain(); - m.what = what; - sendMessage(m, errorMsg); - } - - private void sendMessage(Message m, String errorMsg) { - if (mToControllerMessenger == null) { - return; - } - try { - mToControllerMessenger.send(m); - } catch (RemoteException e) { - Log.e(TAG, errorMsg, e); + mContext.startActivity(settingsIntent); } } - private void cancelDelayedFinish() { - mHandler.removeCallbacks(mFinishRunnable); + private void cancelDelayedHide() { + mHandler.removeCallbacks(mHideMenuRunnable); } - private void repostDelayedFinish(int delay) { + private void repostDelayedHide(int delay) { int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay, FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS); - mHandler.removeCallbacks(mFinishRunnable); - mHandler.postDelayed(mFinishRunnable, recommendedTimeout); + mHandler.removeCallbacks(mHideMenuRunnable); + mHandler.postDelayed(mHideMenuRunnable, recommendedTimeout); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index 19138fdba788..dcee2a5b4b20 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -23,6 +23,8 @@ import android.content.Context; import android.graphics.PointF; import android.graphics.Rect; import android.os.Debug; +import android.os.Handler; +import android.os.Looper; import android.util.Log; import android.view.Choreographer; @@ -66,6 +68,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private PipMenuActivityController mMenuController; private PipSnapAlgorithm mSnapAlgorithm; + private final Handler mMainHandler = new Handler(Looper.getMainLooper()); + /** PIP's current bounds on the screen. */ private final Rect mBounds = new Rect(); @@ -128,8 +132,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> { - mMenuController.updateMenuLayout(newBounds); - mBounds.set(newBounds); + mMainHandler.post(() -> { + mMenuController.updateMenuLayout(newBounds); + mBounds.set(newBounds); + }); }; /** @@ -253,7 +259,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mTemporaryBounds.set(toBounds); mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds, (Rect newBounds) -> { - mMenuController.updateMenuLayout(newBounds); + mMainHandler.post(() -> { + mMenuController.updateMenuLayout(newBounds); + }); }); } } else { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java index 2800bb938149..f6853ecf8ee7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java @@ -111,6 +111,7 @@ public class PipResizeGestureHandler { private InputMonitor mInputMonitor; private InputEventReceiver mInputEventReceiver; private PipTaskOrganizer mPipTaskOrganizer; + private PipMenuActivityController mPipMenuActivityController; private PipUiEventLogger mPipUiEventLogger; private int mCtrlType; @@ -119,7 +120,7 @@ public class PipResizeGestureHandler { PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig, PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable, SysUiState sysUiState, - PipUiEventLogger pipUiEventLogger) { + PipUiEventLogger pipUiEventLogger, PipMenuActivityController menuActivityController) { mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = context.getMainExecutor(); @@ -129,6 +130,7 @@ public class PipResizeGestureHandler { mMovementBoundsSupplier = movementBoundsSupplier; mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; mSysUiState = sysUiState; + mPipMenuActivityController = menuActivityController; mPipUiEventLogger = pipUiEventLogger; context.getDisplay().getRealSize(mMaxSize); @@ -298,6 +300,7 @@ public class PipResizeGestureHandler { float x = ev.getX(); float y = ev.getY(); if (action == MotionEvent.ACTION_DOWN) { + final Rect currentPipBounds = mMotionHelper.getBounds(); mLastResizeBounds.setEmpty(); mAllowGesture = isInValidSysUiState() && isWithinTouchRegion((int) x, (int) y); if (mAllowGesture) { @@ -305,6 +308,10 @@ public class PipResizeGestureHandler { mDownPoint.set(x, y); mLastDownBounds.set(mMotionHelper.getBounds()); } + if (!currentPipBounds.contains((int) ev.getX(), (int) ev.getY()) + && mPipMenuActivityController.isMenuVisible()) { + mPipMenuActivityController.hideMenu(); + } } else if (mAllowGesture) { switch (action) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 1b84c1417c51..9693f235a4ff 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -230,7 +230,7 @@ public class PipTouchHandler { mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper, deviceConfig, pipTaskOrganizer, this::getMovementBounds, - this::updateMovementBounds, sysUiState, pipUiEventLogger); + this::updateMovementBounds, sysUiState, pipUiEventLogger, menuController); mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler, () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(), true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()), @@ -773,10 +773,7 @@ public class PipTouchHandler { * Updates the appearance of the menu and scrim on top of the PiP while dismissing. */ private void updateDismissFraction() { - // Skip updating the dismiss fraction when the IME is showing. This is to work around an - // issue where starting the menu activity for the dismiss overlay will steal the window - // focus, which closes the IME. - if (mMenuController != null && !mIsImeShowing) { + if (mMenuController != null) { Rect bounds = mMotionHelper.getBounds(); final float target = mInsetBounds.bottom; float fraction = 0f; @@ -784,7 +781,7 @@ public class PipTouchHandler { final float distance = bounds.bottom - target; fraction = Math.min(distance / bounds.height(), 1f); } - if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuActivityVisible()) { + if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuVisible()) { // Update if the fraction > 0, or if fraction == 0 and the menu was already visible mMenuController.setDismissFraction(fraction); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 8d0948b16d20..a388fa324a2e 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -142,7 +142,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio // Used to calculate the movement bounds private final DisplayInfo mTmpDisplayInfo = new DisplayInfo(); private final Rect mTmpInsetBounds = new Rect(); - private final Rect mTmpNormalBounds = new Rect(); // Keeps track of the IME visibility to adjust the PiP when the IME is visible private boolean mImeVisible; @@ -216,10 +215,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio public void onMovementBoundsChanged(boolean fromImeAdjustment) { mHandler.post(() -> { // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first. - final Rect destinationBounds = new Rect(); - mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds, - destinationBounds, mTmpDisplayInfo); - mDefaultPipBounds.set(destinationBounds); + mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mPipBounds, + mDefaultPipBounds, mTmpDisplayInfo); }); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java index 214088c99eeb..70374036ef14 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java @@ -36,8 +36,8 @@ import javax.inject.Inject; * Activity to show the PIP menu to control PIP. */ public class PipMenuActivity extends Activity implements PipManager.Listener { - private static final boolean DEBUG = false; private static final String TAG = "PipMenuActivity"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); static final String EXTRA_CUSTOM_ACTIONS = "custom_actions"; diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java index 651a4f367f21..5e5de58da2f6 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java @@ -114,6 +114,14 @@ public class PipNotification { notifyPipNotification(); } } + + @Override + public void onMetadataChanged(MediaMetadata metadata) { + if (updateMediaControllerMetadata() && mNotified) { + // update notification + notifyPipNotification(); + } + } }; private final PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() { diff --git a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java new file mode 100644 index 000000000000..8b8941a9112d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java @@ -0,0 +1,32 @@ +/* + * 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.power.dagger; + +import com.android.systemui.power.PowerNotificationWarnings; +import com.android.systemui.power.PowerUI; + +import dagger.Binds; +import dagger.Module; + + +/** Dagger Module for code in the power package. */ +@Module +public interface PowerModule { + /** */ + @Binds + PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl); +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java index 8740581240b5..8ff96c8a4a37 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java @@ -22,6 +22,7 @@ import android.os.Handler; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.qs.AutoAddTracker; +import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.phone.AutoTileManager; import com.android.systemui.statusbar.phone.ManagedProfileController; @@ -29,6 +30,7 @@ import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.HotspotController; +import dagger.Binds; import dagger.Module; import dagger.Provides; @@ -56,4 +58,9 @@ public interface QSModule { manager.init(); return manager; } + + + /** */ + @Binds + QSHost provideQsHost(QSTileHost controllerImpl); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index c0cc58638e3f..263bbdbf7c35 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -98,6 +98,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.function.BiConsumer; import javax.inject.Inject; @@ -592,6 +593,8 @@ public class OverviewProxyService extends CurrentUserTracker implements }; private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged; + private final BiConsumer<Rect, Rect> mSplitScreenBoundsChangeListener = + this::notifySplitScreenBoundsChanged; // This is the death handler for the binder from the launcher service private final IBinder.DeathRecipient mOverviewServiceDeathRcpt @@ -613,7 +616,6 @@ public class OverviewProxyService extends CurrentUserTracker implements mNavBarControllerLazy = navBarControllerLazy; mStatusBarWinController = statusBarWinController; mConnectionBackoffAttempts = 0; - mSplitScreenOptional = splitScreenOptional; mRecentsComponentName = ComponentName.unflattenFromString(context.getString( com.android.internal.R.string.config_recentsComponentName)); mQuickStepIntent = new Intent(ACTION_QUICKSTEP) @@ -653,6 +655,10 @@ public class OverviewProxyService extends CurrentUserTracker implements }); mCommandQueue = commandQueue; + splitScreenOptional.ifPresent(splitScreen -> + splitScreen.registerBoundsChangeListener(mSplitScreenBoundsChangeListener)); + mSplitScreenOptional = splitScreenOptional; + // Listen for user setup startTracking(); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index 8ec3db59117d..3bf118d1f39f 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -40,7 +40,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.LongRunning; -import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import java.io.IOException; @@ -79,12 +79,12 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis private final Executor mLongExecutor; private final UiEventLogger mUiEventLogger; private final NotificationManager mNotificationManager; - private final CurrentUserContextTracker mUserContextTracker; + private final UserContextProvider mUserContextTracker; @Inject public RecordingService(RecordingController controller, @LongRunning Executor executor, UiEventLogger uiEventLogger, NotificationManager notificationManager, - CurrentUserContextTracker userContextTracker, KeyguardDismissUtil keyguardDismissUtil) { + UserContextProvider userContextTracker, KeyguardDismissUtil keyguardDismissUtil) { mController = controller; mLongExecutor = executor; mUiEventLogger = uiEventLogger; @@ -120,7 +120,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis String action = intent.getAction(); Log.d(TAG, "onStartCommand " + action); - int mCurrentUserId = mUserContextTracker.getCurrentUserContext().getUserId(); + int mCurrentUserId = mUserContextTracker.getUserContext().getUserId(); UserHandle currentUser = new UserHandle(mCurrentUserId); switch (action) { case ACTION_START: @@ -136,7 +136,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis setTapsVisible(mShowTaps); mRecorder = new ScreenMediaRecorder( - mUserContextTracker.getCurrentUserContext(), + mUserContextTracker.getUserContext(), mCurrentUserId, mAudioSource, this @@ -156,7 +156,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis // we want to post the notifications for that user, which is NOT current user int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); if (userId == -1) { - userId = mUserContextTracker.getCurrentUserContext().getUserId(); + userId = mUserContextTracker.getUserContext().getUserId(); } Log.d(TAG, "notifying for user " + userId); stopRecording(userId); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java index dc47ab4dff63..2b62a29587e6 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java @@ -35,7 +35,7 @@ import android.widget.Spinner; import android.widget.Switch; import com.android.systemui.R; -import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.settings.UserContextProvider; import java.util.ArrayList; import java.util.List; @@ -51,7 +51,7 @@ public class ScreenRecordDialog extends Activity { private static final String TAG = "ScreenRecordDialog"; private final RecordingController mController; - private final CurrentUserContextTracker mCurrentUserContextTracker; + private final UserContextProvider mUserContextProvider; private Switch mTapsSwitch; private Switch mAudioSwitch; private Spinner mOptions; @@ -59,9 +59,9 @@ public class ScreenRecordDialog extends Activity { @Inject public ScreenRecordDialog(RecordingController controller, - CurrentUserContextTracker currentUserContextTracker) { + UserContextProvider userContextProvider) { mController = controller; - mCurrentUserContextTracker = currentUserContextTracker; + mUserContextProvider = userContextProvider; } @Override @@ -108,7 +108,7 @@ public class ScreenRecordDialog extends Activity { } private void requestScreenCapture() { - Context userContext = mCurrentUserContextTracker.getCurrentUserContext(); + Context userContext = mUserContextProvider.getUserContext(); boolean showTaps = mTapsSwitch.isChecked(); ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked() ? (ScreenRecordingAudioSource) mOptions.getSelectedItem() diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index e24fbc6cca9d..7dd4edd233bd 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -573,7 +573,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect, Insets screenInsets, boolean showFlash) { - dismissScreenshot("new screenshot requested", true); + if (mScreenshotLayout.isAttachedToWindow()) { + if (!mDismissAnimation.isRunning()) { // if we didn't already dismiss for another reason + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED); + } + dismissScreenshot("new screenshot requested", true); + } mScreenBitmap = screenshot; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java index 6b42f2e07bc3..74e0229c4992 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java @@ -59,7 +59,9 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum { @UiEvent(doc = "screenshot interaction timed out") SCREENSHOT_INTERACTION_TIMEOUT(310), @UiEvent(doc = "screenshot explicitly dismissed") - SCREENSHOT_EXPLICIT_DISMISSAL(311); + SCREENSHOT_EXPLICIT_DISMISSAL(311), + @UiEvent(doc = "screenshot reentered for new screenshot") + SCREENSHOT_REENTERED(640); private final int mId; diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt deleted file mode 100644 index d7c4caaa4f9d..000000000000 --- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.settings - -import android.content.ContentResolver -import android.content.Context -import android.os.UserHandle -import androidx.annotation.VisibleForTesting -import com.android.systemui.broadcast.BroadcastDispatcher -import com.android.systemui.util.Assert -import java.lang.IllegalStateException - -/** - * Tracks a reference to the context for the current user - * - * Constructor is injected at SettingsModule - */ -class CurrentUserContextTracker internal constructor( - private val sysuiContext: Context, - broadcastDispatcher: BroadcastDispatcher -) : CurrentUserContentResolverProvider { - private val userTracker: CurrentUserTracker - private var initialized = false - - private var _curUserContext: Context? = null - val currentUserContext: Context - get() { - if (!initialized) { - throw IllegalStateException("Must initialize before getting context") - } - return _curUserContext!! - } - - override val currentUserContentResolver: ContentResolver - get() = currentUserContext.contentResolver - - init { - userTracker = object : CurrentUserTracker(broadcastDispatcher) { - override fun onUserSwitched(newUserId: Int) { - handleUserSwitched(newUserId) - } - } - } - - fun initialize() { - initialized = true - userTracker.startTracking() - _curUserContext = makeUserContext(userTracker.currentUserId) - } - - @VisibleForTesting - fun handleUserSwitched(newUserId: Int) { - _curUserContext = makeUserContext(newUserId) - } - - private fun makeUserContext(uid: Int): Context { - Assert.isMainThread() - return sysuiContext.createContextAsUser(UserHandle.of(uid), 0) - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt index 9d05843b42bf..e0c0c15cba31 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt @@ -18,7 +18,10 @@ package com.android.systemui.settings import android.content.ContentResolver -interface CurrentUserContentResolverProvider { +/** + * Implemented by [UserTrackerImpl]. + */ +interface UserContentResolverProvider { - val currentUserContentResolver: ContentResolver + val userContentResolver: ContentResolver }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt new file mode 100644 index 000000000000..27af15222327 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt @@ -0,0 +1,26 @@ +/* + * 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.settings + +import android.content.Context + +/** + * Implemented by [UserTrackerImpl]. + */ +interface UserContextProvider { + val userContext: Context +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt new file mode 100644 index 000000000000..26d408fe4ab7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt @@ -0,0 +1,74 @@ +/* + * 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.settings + +import android.content.Context +import android.content.pm.UserInfo +import android.os.UserHandle +import java.util.concurrent.Executor + +/** + * User tracker for SystemUI. + * + * This tracker provides async access to current user information, as well as callbacks for + * user/profile change. + */ +interface UserTracker : UserContentResolverProvider, UserContextProvider { + + /** + * Current user's id. + */ + val userId: Int + + /** + * [UserHandle] for current user + */ + val userHandle: UserHandle + + /** + * List of profiles associated with the current user. + */ + val userProfiles: List<UserInfo> + + /** + * Add a [Callback] to be notified of chances, on a particular [Executor] + */ + fun addCallback(callback: Callback, executor: Executor) + + /** + * Remove a [Callback] previously added. + */ + fun removeCallback(callback: Callback) + + /** + * Ćallback for notifying of changes. + */ + interface Callback { + + /** + * Notifies that the current user has changed. + */ + @JvmDefault + fun onUserChanged(newUser: Int, userContext: Context) {} + + /** + * Notifies that the current user's profiles have changed. + */ + @JvmDefault + fun onProfilesChanged(profiles: List<UserInfo>) {} + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt new file mode 100644 index 000000000000..4cc0eeee712c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt @@ -0,0 +1,240 @@ +/* + * 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.settings + +import android.content.BroadcastReceiver +import android.content.ContentResolver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.UserInfo +import android.os.Handler +import android.os.UserHandle +import android.os.UserManager +import android.util.Log +import androidx.annotation.GuardedBy +import androidx.annotation.WorkerThread +import com.android.systemui.Dumpable +import com.android.systemui.dump.DumpManager +import com.android.systemui.util.Assert +import java.io.FileDescriptor +import java.io.PrintWriter +import java.lang.IllegalStateException +import java.lang.ref.WeakReference +import java.util.concurrent.Executor +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +/** + * SystemUI cache for keeping track of the current user and associated values. + * + * The values provided asynchronously are NOT copies, but shared among all requesters. Do not + * modify them. + * + * This class purposefully doesn't use [BroadcastDispatcher] in order to receive the broadcast as + * soon as possible (and reduce its dependency graph). + * Other classes that want to listen to the broadcasts listened here SHOULD + * subscribe to this class instead. + * + * @see UserTracker + * + * Class constructed and initialized in [SettingsModule]. + */ +class UserTrackerImpl internal constructor( + private val context: Context, + private val userManager: UserManager, + private val dumpManager: DumpManager, + private val backgroundHandler: Handler +) : UserTracker, Dumpable, BroadcastReceiver() { + + companion object { + private const val TAG = "UserTrackerImpl" + } + + var initialized = false + private set + + private val mutex = Any() + + override var userId: Int by SynchronizedDelegate(context.userId) + private set + + override var userHandle: UserHandle by SynchronizedDelegate(context.user) + private set + + override var userContext: Context by SynchronizedDelegate(context) + private set + + override val userContentResolver: ContentResolver + get() = userContext.contentResolver + + /** + * Returns a [List<UserInfo>] of all profiles associated with the current user. + * + * The list returned is not a copy, so a copy should be made if its elements need to be + * modified. + */ + override var userProfiles: List<UserInfo> by SynchronizedDelegate(emptyList()) + private set + + @GuardedBy("callbacks") + private val callbacks: MutableList<DataItem> = ArrayList() + + fun initialize(startingUser: Int) { + if (initialized) { + return + } + initialized = true + setUserIdInternal(startingUser) + + val filter = IntentFilter().apply { + addAction(Intent.ACTION_USER_SWITCHED) + addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) + addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED) + } + context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler) + + dumpManager.registerDumpable(TAG, this) + } + + override fun onReceive(context: Context, intent: Intent) { + when (intent.action) { + Intent.ACTION_USER_SWITCHED -> { + handleSwitchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)) + } + Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE -> { + handleProfilesChanged() + } + } + } + + private fun setUserIdInternal(user: Int): Pair<Context, List<UserInfo>> { + val profiles = userManager.getProfiles(user) + val handle = UserHandle(user) + val ctx = context.createContextAsUser(handle, 0) + + synchronized(mutex) { + userId = user + userHandle = handle + userContext = ctx + userProfiles = profiles.map { UserInfo(it) } + } + return ctx to profiles + } + + @WorkerThread + private fun handleSwitchUser(newUser: Int) { + Assert.isNotMainThread() + if (newUser == UserHandle.USER_NULL) { + Log.w(TAG, "handleSwitchUser - Couldn't get new id from intent") + return + } + + if (newUser == userId) return + Log.i(TAG, "Switching to user $newUser") + + val (ctx, profiles) = setUserIdInternal(newUser) + + notifySubscribers { + onUserChanged(newUser, ctx) + onProfilesChanged(profiles) + } + } + + @WorkerThread + private fun handleProfilesChanged() { + Assert.isNotMainThread() + + val profiles = userManager.getProfiles(userId) + synchronized(mutex) { + userProfiles = profiles.map { UserInfo(it) } // save a "deep" copy + } + notifySubscribers { + onProfilesChanged(profiles) + } + } + + override fun addCallback(callback: UserTracker.Callback, executor: Executor) { + synchronized(callbacks) { + callbacks.add(DataItem(WeakReference(callback), executor)) + } + } + + override fun removeCallback(callback: UserTracker.Callback) { + synchronized(callbacks) { + callbacks.removeIf { it.sameOrEmpty(callback) } + } + } + + private inline fun notifySubscribers(crossinline action: UserTracker.Callback.() -> Unit) { + val list = synchronized(callbacks) { + callbacks.toList() + } + list.forEach { + if (it.callback.get() != null) { + it.executor.execute { + it.callback.get()?.action() + } + } + } + } + + override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { + pw.println("Initialized: $initialized") + if (initialized) { + pw.println("userId: $userId") + val ids = userProfiles.map { it.id } + pw.println("userProfiles: $ids") + } + val list = synchronized(callbacks) { + callbacks.toList() + } + pw.println("Callbacks:") + list.forEach { + it.callback.get()?.let { + pw.println(" $it") + } + } + } + + private class SynchronizedDelegate<T : Any>( + private var value: T + ) : ReadWriteProperty<UserTrackerImpl, T> { + + @GuardedBy("mutex") + override fun getValue(thisRef: UserTrackerImpl, property: KProperty<*>): T { + if (!thisRef.initialized) { + throw IllegalStateException("Must initialize before getting ${property.name}") + } + return synchronized(thisRef.mutex) { value } + } + + @GuardedBy("mutex") + override fun setValue(thisRef: UserTrackerImpl, property: KProperty<*>, value: T) { + synchronized(thisRef.mutex) { this.value = value } + } + } +} + +private data class DataItem( + val callback: WeakReference<UserTracker.Callback>, + val executor: Executor +) { + fun sameOrEmpty(other: UserTracker.Callback): Boolean { + return callback.get()?.equals(other) ?: true + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java index b1ed77275187..7084d3ffc9ff 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java +++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java @@ -16,12 +16,18 @@ package com.android.systemui.settings.dagger; +import android.app.ActivityManager; import android.content.Context; +import android.os.Handler; +import android.os.UserManager; -import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.settings.CurrentUserContentResolverProvider; -import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.settings.UserContentResolverProvider; +import com.android.systemui.settings.UserContextProvider; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.settings.UserTrackerImpl; import dagger.Binds; import dagger.Module; @@ -33,22 +39,27 @@ import dagger.Provides; @Module public abstract class SettingsModule { - /** - * Provides and initializes a CurrentUserContextTracker - */ + + @Binds + @SysUISingleton + abstract UserContextProvider bindUserContextProvider(UserTracker tracker); + + @Binds + @SysUISingleton + abstract UserContentResolverProvider bindUserContentResolverProvider( + UserTracker tracker); + @SysUISingleton @Provides - static CurrentUserContextTracker provideCurrentUserContextTracker( + static UserTracker provideUserTracker( Context context, - BroadcastDispatcher broadcastDispatcher) { - CurrentUserContextTracker tracker = - new CurrentUserContextTracker(context, broadcastDispatcher); - tracker.initialize(); + UserManager userManager, + DumpManager dumpManager, + @Background Handler handler + ) { + int startingUser = ActivityManager.getCurrentUser(); + UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, dumpManager, handler); + tracker.initialize(startingUser); return tracker; } - - @Binds - @SysUISingleton - abstract CurrentUserContentResolverProvider bindCurrentUserContentResolverTracker( - CurrentUserContextTracker tracker); } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 95f048b0b06d..fdf24b1e1a7e 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -62,10 +62,8 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; import com.android.internal.policy.DockedDividerUtils; -import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.statusbar.FlingAnimationUtils; import java.util.function.Consumer; @@ -138,6 +136,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, private final Rect mOtherInsetRect = new Rect(); private final Rect mLastResizeRect = new Rect(); private final Rect mTmpRect = new Rect(); + private SplitScreenController mSplitScreenController; private WindowManagerProxy mWindowManagerProxy; private DividerWindowManager mWindowManager; private VelocityTracker mVelocityTracker; @@ -353,9 +352,11 @@ public class DividerView extends FrameLayout implements OnTouchListener, } } - public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState, + void injectDependencies(SplitScreenController splitScreenController, + DividerWindowManager windowManager, DividerState dividerState, DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl, DividerImeController imeController, WindowManagerProxy wmProxy) { + mSplitScreenController = splitScreenController; mWindowManager = windowManager; mState = dividerState; mCallback = callback; @@ -697,8 +698,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, mTmpRect.top = 0; break; } - Dependency.get(OverviewProxyService.class) - .notifySplitScreenBoundsChanged(mOtherTaskRect, mTmpRect); + mSplitScreenController.notifyBoundsChanged(mOtherTaskRect, mTmpRect); } private void cancelFlingAnimation() { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreen.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreen.java index 91850cc46ab6..93b1f86a5dc2 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreen.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreen.java @@ -16,9 +16,11 @@ package com.android.systemui.stackdivider; +import android.graphics.Rect; import android.window.WindowContainerToken; import java.io.PrintWriter; +import java.util.function.BiConsumer; import java.util.function.Consumer; /** @@ -81,6 +83,9 @@ public interface SplitScreen { /** Registers listener that gets called whenever the existence of the divider changes. */ void registerInSplitScreenListener(Consumer<Boolean> listener); + /** Registers listener that gets called whenever the split screen bounds changes. */ + void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener); + /** @return the container token for the secondary split root task. */ WindowContainerToken getSecondaryRoot(); } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenController.java index 11773f6d358c..360b49555612 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenController.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenController.java @@ -46,6 +46,7 @@ import com.android.wm.shell.common.TransactionPool; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.function.BiConsumer; import java.util.function.Consumer; /** @@ -73,6 +74,8 @@ public class SplitScreenController implements SplitScreen, private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners = new ArrayList<>(); + private final ArrayList<WeakReference<BiConsumer<Rect, Rect>>> mBoundsChangedListeners = + new ArrayList<>(); private DividerWindowManager mWindowManager; @@ -257,8 +260,8 @@ public class SplitScreenController implements SplitScreen, mView = (DividerView) LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null); DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId()); - mView.injectDependencies(mWindowManager, mDividerState, mForcedResizableController, mSplits, - mSplitLayout, mImePositionProcessor, mWindowManagerProxy); + mView.injectDependencies(this, mWindowManager, mDividerState, mForcedResizableController, + mSplits, mSplitLayout, mImePositionProcessor, mWindowManagerProxy); mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE); mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */); final int size = dctx.getResources().getDimensionPixelSize( @@ -466,6 +469,24 @@ public class SplitScreenController implements SplitScreen, } } + @Override + public void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) { + synchronized (mBoundsChangedListeners) { + mBoundsChangedListeners.add(new WeakReference<>(listener)); + } + } + + /** Notifies the bounds of split screen changed. */ + void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) { + synchronized (mBoundsChangedListeners) { + mBoundsChangedListeners.removeIf(wf -> { + BiConsumer<Rect, Rect> l = wf.get(); + if (l != null) l.accept(secondaryWindowBounds, secondaryWindowInsets); + return l == null; + }); + } + } + void startEnterSplit() { update(mDisplayController.getDisplayContext( mContext.getDisplayId()).getResources().getConfiguration()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java index eca4c8082dfe..0df69a0a1f43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java @@ -185,6 +185,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim mAlertEntries.put(entry.getKey(), alertEntry); onAlertEntryAdded(alertEntry); entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); + entry.setIsAlerting(true); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java index 4fa782269c2d..e61e05a7dc2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -163,7 +165,7 @@ public class DragDownHelper implements Gefingerpoken { if (!mDragDownCallback.isFalsingCheckNeeded()) { return false; } - return mFalsingManager.isFalseTouch() || !mDraggedFarEnough; + return mFalsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN) || !mDraggedFarEnough; } private void captureStartingChild(float x, float y) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index ba54d1bff6e8..6fa3633acc43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -30,6 +30,7 @@ import android.view.ViewConfiguration import com.android.systemui.Gefingerpoken import com.android.systemui.Interpolators import com.android.systemui.R +import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -106,7 +107,7 @@ constructor( private var velocityTracker: VelocityTracker? = null private val isFalseTouch: Boolean - get() = falsingManager.isFalseTouch + get() = falsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN) var qsExpanded: Boolean = false var pulseExpandAbortListener: Runnable? = null var bouncerShowing: Boolean = false diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java index 44550b72e521..db2875a3d9aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -37,6 +37,8 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.SmartReplyController; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.DynamicChildBindController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; @@ -45,13 +47,19 @@ import com.android.systemui.statusbar.notification.collection.inflation.LowPrior import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.ManagedProfileController; +import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; +import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.concurrency.DelayableExecutor; +import dagger.Binds; import dagger.Lazy; import dagger.Module; import dagger.Provides; @@ -136,6 +144,12 @@ public interface StatusBarDependenciesModule { return new SmartReplyController(entryManager, statusBarService, clickNotifier); } + + /** */ + @Binds + NotificationRemoteInputManager.Callback provideNotificationRemoteInputManagerCallback( + StatusBarRemoteInputCallback callbackImpl); + /** */ @SysUISingleton @Provides @@ -179,4 +193,22 @@ public interface StatusBarDependenciesModule { static CommandQueue provideCommandQueue(Context context, ProtoTracer protoTracer) { return new CommandQueue(context, protoTracer); } + + /** + */ + @Binds + ManagedProfileController provideManagedProfileController( + ManagedProfileControllerImpl controllerImpl); + + /** + */ + @Binds + SysuiStatusBarStateController providesSysuiStatusBarStateController( + StatusBarStateControllerImpl statusBarStateControllerImpl); + + /** + */ + @Binds + StatusBarIconController provideStatusBarIconController( + StatusBarIconControllerImpl controllerImpl); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 387247eb6c5b..8ce9d944b865 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -177,6 +177,7 @@ public final class NotificationEntry extends ListEntry { @Nullable private Long mPendingAnimationDuration; private boolean mIsMarkedForUserTriggeredMovement; private boolean mShelfIconVisible; + private boolean mIsAlerting; /** * @param sbn the StatusBarNotification from system server @@ -955,6 +956,14 @@ public final class NotificationEntry extends ListEntry { mIsMarkedForUserTriggeredMovement = marked; } + public void setIsAlerting(boolean isAlerting) { + mIsAlerting = isAlerting; + } + + public boolean isAlerting() { + return mIsAlerting; + } + /** Information about a suggestion that is being edited. */ public static class EditedSuggestionInfo { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 6d01324f1b7b..f3ed95bd2d76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -34,7 +34,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -127,7 +127,7 @@ public interface NotificationsModule { LauncherApps launcherApps, ShortcutManager shortcutManager, ChannelEditorDialogController channelEditorDialogController, - CurrentUserContextTracker contextTracker, + UserContextProvider contextTracker, Provider<PriorityOnboardingDialogController.Builder> builderProvider, AssistantFeedbackController assistantFeedbackController, BubbleController bubbleController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 60074f608969..7d418f30e4c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -52,7 +52,7 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.NotificationLifetimeExtender; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; @@ -121,7 +121,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx private final INotificationManager mNotificationManager; private final LauncherApps mLauncherApps; private final ShortcutManager mShortcutManager; - private final CurrentUserContextTracker mContextTracker; + private final UserContextProvider mContextTracker; private final Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider; private final UiEventLogger mUiEventLogger; @@ -138,7 +138,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx LauncherApps launcherApps, ShortcutManager shortcutManager, ChannelEditorDialogController channelEditorDialogController, - CurrentUserContextTracker contextTracker, + UserContextProvider contextTracker, Provider<PriorityOnboardingDialogController.Builder> builderProvider, AssistantFeedbackController assistantFeedbackController, BubbleController bubbleController, @@ -484,7 +484,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx onSettingsClick, onSnoozeClickListener, iconFactoryLoader, - mContextTracker.getCurrentUserContext(), + mContextTracker.getUserContext(), mBuilderProvider, mDeviceProvisionedController.isDeviceProvisioned(), mMainHandler, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index d5e55315ff0a..0302b2b450e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -81,17 +81,17 @@ public class AmbientState { private boolean mAppearing; private float mPulseHeight = MAX_PULSE_HEIGHT; private float mDozeAmount = 0.0f; - private HeadsUpManager mHeadUpManager; private Runnable mOnPulseHeightChangedListener; private ExpandableNotificationRow mTrackedHeadsUpRow; private float mAppearFraction; + /** Tracks the state from AlertingNotificationManager#hasNotifications() */ + private boolean mHasAlertEntries; + public AmbientState( Context context, - @NonNull SectionProvider sectionProvider, - HeadsUpManager headsUpManager) { + @NonNull SectionProvider sectionProvider) { mSectionProvider = sectionProvider; - mHeadUpManager = headsUpManager; reload(context); } @@ -393,7 +393,7 @@ public class AmbientState { } public boolean hasPulsingNotifications() { - return mPulsing && mHeadUpManager != null && mHeadUpManager.hasNotifications(); + return mPulsing && mHasAlertEntries; } public void setPulsing(boolean hasPulsing) { @@ -408,10 +408,7 @@ public class AmbientState { } public boolean isPulsing(NotificationEntry entry) { - if (!mPulsing || mHeadUpManager == null) { - return false; - } - return mHeadUpManager.isAlerting(entry.getKey()); + return mPulsing && entry.isAlerting(); } public boolean isPanelTracking() { @@ -568,4 +565,8 @@ public class AmbientState { public float getAppearFraction() { return mAppearFraction; } + + public void setHasAlertEntries(boolean hasAlertEntries) { + mHasAlertEntries = hasAlertEntries; + } } 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 b1a9efe40fdb..3370773df807 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 @@ -92,8 +92,6 @@ import com.android.systemui.Dumpable; import com.android.systemui.ExpandHelper; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper.DragDownCallback; @@ -102,7 +100,6 @@ import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelfController; -import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; @@ -127,19 +124,14 @@ import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; -import com.android.systemui.statusbar.notification.row.NotificationGuts; -import com.android.systemui.statusbar.notification.row.NotificationGutsManager; -import com.android.systemui.statusbar.notification.row.NotificationSnooze; import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener; import com.android.systemui.statusbar.phone.NotificationPanelViewController; -import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpUtil; @@ -340,13 +332,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>(); private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations = new HashSet<>(); - private HeadsUpManagerPhone mHeadsUpManager; private final NotificationRoundnessManager mRoundnessManager; private boolean mTrackingHeadsUp; - private ScrimController mScrimController; private boolean mForceNoOverlappingRendering; private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>(); - private FalsingManager mFalsingManager; private boolean mAnimationRunning; private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater = new ViewTreeObserver.OnPreDrawListener() { @@ -499,7 +488,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN; private NotificationPanelViewController mNotificationPanelController; - private final NotificationGutsManager mNotificationGutsManager; private final NotificationSectionsManager mSectionsManager; private final ForegroundServiceSectionController mFgsSectionController; private ForegroundServiceDungeonView mFgsSectionView; @@ -513,6 +501,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private NotificationStackScrollLayoutController mController; private boolean mKeyguardMediaControllorVisible; + private NotificationEntry mTopHeadsUpEntry; + private long mNumHeadsUp; + private NotificationStackScrollLayoutController.TouchHandler mTouchHandler; private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener = new ExpandableView.OnHeightChangedListener() { @@ -562,9 +553,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable NotificationRoundnessManager notificationRoundnessManager, DynamicPrivacyController dynamicPrivacyController, SysuiStatusBarStateController statusbarStateController, - HeadsUpManagerPhone headsUpManager, - FalsingManager falsingManager, - NotificationGutsManager notificationGutsManager, NotificationSectionsManager notificationSectionsManager, ForegroundServiceSectionController fgsSectionController, ForegroundServiceDismissalFeatureController fgsFeatureController, @@ -578,11 +566,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable Resources res = getResources(); mRoundnessManager = notificationRoundnessManager; - - mNotificationGutsManager = notificationGutsManager; - mHeadsUpManager = headsUpManager; - mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed); - mFalsingManager = falsingManager; mFgsSectionController = fgsSectionController; mSectionsManager = notificationSectionsManager; @@ -594,7 +577,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable }); mSections = mSectionsManager.createSectionsForBuckets(); - mAmbientState = new AmbientState(context, mSectionsManager, mHeadsUpManager); + mAmbientState = new AmbientState(context, mSectionsManager); mBgColor = context.getColor(R.color.notification_shade_background_color); int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height); @@ -750,27 +733,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return false; } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public RemoteInputController.Delegate createDelegate() { - return new RemoteInputController.Delegate() { - public void setRemoteInputActive(NotificationEntry entry, - boolean remoteInputActive) { - mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive); - entry.notifyHeightChanged(true /* needsAnimation */); - updateFooter(); - } - - public void lockScrollTo(NotificationEntry entry) { - NotificationStackScrollLayout.this.lockScrollTo(entry.getRow()); - } - - public void requestDisallowLongPressAndDismiss() { - requestDisallowLongPress(); - requestDisallowDismiss(); - } - }; - } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public NotificationSwipeActionHelper getSwipeActionHelper() { return mSwipeHelper; @@ -956,7 +918,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void updateBackgroundDimming() { + void updateBackgroundDimming() { // No need to update the background color if it's not being drawn. if (!mShouldDrawNotificationBackground) { return; @@ -1472,11 +1434,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ @ShadeViewRefactor(RefactorComponent.COORDINATOR) private int getTopHeadsUpPinnedHeight() { - NotificationEntry topEntry = mHeadsUpManager.getTopEntry(); - if (topEntry == null) { + if (mTopHeadsUpEntry == null) { return 0; } - ExpandableNotificationRow row = topEntry.getRow(); + ExpandableNotificationRow row = mTopHeadsUpEntry.getRow(); if (row.isChildInGroup()) { final NotificationEntry groupSummary = mGroupManager.getGroupSummary(row.getEntry().getSbn()); @@ -1497,7 +1458,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable int visibleNotifCount = getVisibleNotificationCount(); if (mEmptyShadeView.getVisibility() == GONE && visibleNotifCount > 0) { if (isHeadsUpTransition() - || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDozing())) { + || (mInHeadsUpPinnedMode && !mAmbientState.isDozing())) { if (mShelf.getVisibility() != GONE && visibleNotifCount > 1) { appearPosition += mShelf.getIntrinsicHeight() + mPaddingBetweenElements; } @@ -1655,9 +1616,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild; NotificationEntry entry = row.getEntry(); if (!mIsExpanded && row.isHeadsUp() && row.isPinned() - && mHeadsUpManager.getTopEntry().getRow() != row + && mTopHeadsUpEntry.getRow() != row && mGroupManager.getGroupSummary( - mHeadsUpManager.getTopEntry().getSbn()) + mTopHeadsUpEntry.getSbn()) != entry) { continue; } @@ -2285,7 +2246,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable // In current design, it only use the top HUN to treat all of HUNs // although there are more than one HUNs int contentHeight = mContentHeight; - if (!isExpanded() && mHeadsUpManager.hasPinnedHeadsUp()) { + if (!isExpanded() && mInHeadsUpPinnedMode) { contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight(); } int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight); @@ -2636,7 +2597,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable false /* shiftPulsingWithFirst */); minTopPosition = firstVisibleSection.getBounds().top; } - boolean shiftPulsingWithFirst = mHeadsUpManager.getAllEntries().count() <= 1 + boolean shiftPulsingWithFirst = mNumHeadsUp <= 1 && (mAmbientState.isDozing() || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard)); for (NotificationSection section : mSections) { @@ -3511,7 +3472,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable // Only animate if we still have pinned heads up, otherwise we just have the // regular collapse animation of the lock screen || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard() - && mHeadsUpManager.hasPinnedHeadsUp()); + && mInHeadsUpPinnedMode); if (performDisappearAnimation && !isHeadsUp) { type = row.wasJustClicked() ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK @@ -3754,58 +3715,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public boolean onTouchEvent(MotionEvent ev) { - NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); - boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL - || ev.getActionMasked() == MotionEvent.ACTION_UP; - handleEmptySpaceClick(ev); - boolean expandWantsIt = false; - boolean swipingInProgress = mSwipingInProgress; - if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion && guts == null) { - if (isCancelOrUp) { - mExpandHelper.onlyObserveMovements(false); - } - boolean wasExpandingBefore = mExpandingNotification; - expandWantsIt = mExpandHelper.onTouchEvent(ev); - if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore - && !mDisallowScrollingInThisMotion) { - dispatchDownEventToScroller(ev); - } - } - boolean scrollerWantsIt = false; - if (mIsExpanded && !swipingInProgress && !mExpandingNotification - && !mDisallowScrollingInThisMotion) { - scrollerWantsIt = onScrollTouch(ev); - } - boolean horizontalSwipeWantsIt = false; - if (!mIsBeingDragged - && !mExpandingNotification - && !mExpandedInThisMotion - && !mOnlyScrollingInThisMotion - && !mDisallowDismissInThisMotion) { - horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev); + if (mTouchHandler != null && mTouchHandler.onTouchEvent(ev)) { + return true; } - // Check if we need to clear any snooze leavebehinds - if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts) - && guts.getGutsContent() instanceof NotificationSnooze) { - NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent(); - if ((ns.isExpanded() && isCancelOrUp) - || (!horizontalSwipeWantsIt && scrollerWantsIt)) { - // If the leavebehind is expanded we clear it on the next up event, otherwise we - // clear it on the next non-horizontal swipe or expand event. - checkSnoozeLeavebehind(); - } - } - if (ev.getActionMasked() == MotionEvent.ACTION_UP) { - mCheckForLeavebehind = true; - } - return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev); + return super.onTouchEvent(ev); } @ShadeViewRefactor(RefactorComponent.INPUT) - private void dispatchDownEventToScroller(MotionEvent ev) { + void dispatchDownEventToScroller(MotionEvent ev) { MotionEvent downEvent = MotionEvent.obtain(ev); downEvent.setAction(MotionEvent.ACTION_DOWN); onScrollTouch(downEvent); @@ -3854,7 +3773,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.INPUT) - private boolean onScrollTouch(MotionEvent ev) { + boolean onScrollTouch(MotionEvent ev) { if (!isScrollingEnabled()) { return false; } @@ -3943,7 +3862,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable customOverScrollBy((int) scrollAmount, mOwnScrollY, range, getHeight() / 2); // If we're scrolling, leavebehinds should be dismissed - checkSnoozeLeavebehind(); + mController.checkSnoozeLeavebehind(); } } break; @@ -4061,44 +3980,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @Override @ShadeViewRefactor(RefactorComponent.INPUT) public boolean onInterceptTouchEvent(MotionEvent ev) { - initDownStates(ev); - handleEmptySpaceClick(ev); - - NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); - boolean expandWantsIt = false; - boolean swipingInProgress = mSwipingInProgress; - if (!swipingInProgress && !mOnlyScrollingInThisMotion && guts == null) { - expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev); - } - boolean scrollWantsIt = false; - if (!swipingInProgress && !mExpandingNotification) { - scrollWantsIt = onInterceptTouchEventScroll(ev); - } - boolean swipeWantsIt = false; - if (!mIsBeingDragged - && !mExpandingNotification - && !mExpandedInThisMotion - && !mOnlyScrollingInThisMotion - && !mDisallowDismissInThisMotion) { - swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev); - } - // Check if we need to clear any snooze leavebehinds - boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP; - if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt && - !expandWantsIt && !scrollWantsIt) { - mCheckForLeavebehind = false; - mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, - false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, - false /* resetMenu */); - } - if (ev.getActionMasked() == MotionEvent.ACTION_UP) { - mCheckForLeavebehind = true; + if (mTouchHandler != null && mTouchHandler.onInterceptTouchEvent(ev)) { + return true; } - return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev); + return super.onInterceptTouchEvent(ev); } @ShadeViewRefactor(RefactorComponent.INPUT) - private void handleEmptySpaceClick(MotionEvent ev) { + void handleEmptySpaceClick(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_MOVE: final float touchSlop = getTouchSlop(ev); @@ -4117,7 +4006,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.INPUT) - private void initDownStates(MotionEvent ev) { + void initDownStates(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { mExpandedInThisMotion = false; mOnlyScrollingInThisMotion = !mScroller.isFinished(); @@ -4139,7 +4028,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.INPUT) - private boolean onInterceptTouchEventScroll(MotionEvent ev) { + boolean onInterceptTouchEventScroll(MotionEvent ev) { if (!isScrollingEnabled()) { return false; } @@ -4335,29 +4224,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.INPUT) - public void closeControlsIfOutsideTouch(MotionEvent ev) { - NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); - NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow(); - View translatingParentView = mSwipeHelper.getTranslatingParentView(); - View view = null; - if (guts != null && !guts.getGutsContent().isLeavebehind()) { - // Only close visible guts if they're not a leavebehind. - view = guts; - } else if (menuRow != null && menuRow.isMenuVisible() - && translatingParentView != null) { - // Checking menu - view = translatingParentView; - } - if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) { - // Touch was outside visible guts / menu notification, close what's visible - mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */, - false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */, - false /* resetMenu */); - resetExposedMenuView(true /* animate */, true /* force */); - } - } - - @ShadeViewRefactor(RefactorComponent.INPUT) void setSwipingInProgress(boolean swiping) { mSwipingInProgress = swiping; if (swiping) { @@ -4392,32 +4258,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return Math.max(mMaxLayoutHeight - mContentHeight, 0); } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - void checkSnoozeLeavebehind() { - if (mCheckForLeavebehind) { - mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, - false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, - false /* resetMenu */); - mCheckForLeavebehind = false; - } - } - - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - void resetCheckSnoozeLeavebehind() { - mCheckForLeavebehind = true; - } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) void onExpansionStarted() { mIsExpansionChanging = true; mAmbientState.setExpansionChanging(true); - checkSnoozeLeavebehind(); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) void onExpansionStopped() { mIsExpansionChanging = false; - resetCheckSnoozeLeavebehind(); mAmbientState.setExpansionChanging(false); if (!mIsExpanded) { resetScrollPosition(); @@ -5202,12 +5051,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setScrimController(ScrimController scrimController) { - mScrimController = scrimController; - mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming); - } - - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void forceNoOverlappingRendering(boolean force) { mForceNoOverlappingRendering = force; } @@ -5811,23 +5654,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mCurrentUserId = userId; } - void onMenuShown(View row) { - mSwipeHelper.onMenuShown(row); - } - - void onMenuReset(View row) { - View translatingParentView = mSwipeHelper.getTranslatingParentView(); - if (translatingParentView != null && row == translatingParentView) { - mSwipeHelper.clearExposedMenuView(); - mSwipeHelper.clearTranslatingParentView(); - if (row instanceof ExpandableNotificationRow) { - mHeadsUpManager.setMenuShown( - ((ExpandableNotificationRow) row).getEntry(), false); - - } - } - } - void addSwipedOutView(View v) { mSwipedOutViews.add(v); } @@ -5840,6 +5666,67 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mAmbientState.onDragFinished(view); } + void setTopHeadsUpEntry(NotificationEntry topEntry) { + mTopHeadsUpEntry = topEntry; + } + + void setNumHeadsUp(long numHeadsUp) { + mNumHeadsUp = numHeadsUp; + mAmbientState.setHasAlertEntries(numHeadsUp > 0); + } + + boolean getSwipingInProgress() { + return mSwipingInProgress; + } + + public boolean getIsExpanded() { + return mIsExpanded; + } + + boolean getOnlyScrollingInThisMotion() { + return mOnlyScrollingInThisMotion; + } + + ExpandHelper getExpandHelper() { + return mExpandHelper; + } + + boolean isExpandingNotification() { + return mExpandingNotification; + } + + boolean getDisallowScrollingInThisMotion() { + return mDisallowScrollingInThisMotion; + } + + boolean isBeingDragged() { + return mIsBeingDragged; + } + + boolean getExpandedInThisMotion() { + return mExpandedInThisMotion; + } + + boolean getDisallowDismissInThisMotion() { + return mDisallowDismissInThisMotion; + } + + void setCheckForLeaveBehind(boolean checkForLeaveBehind) { + mCheckForLeavebehind = checkForLeaveBehind; + } + + void setTouchHandler(NotificationStackScrollLayoutController.TouchHandler touchHandler) { + mTouchHandler = touchHandler; + } + + boolean isSwipingInProgress() { + return mSwipingInProgress; + } + + boolean getCheckSnoozeLeaveBehind() { + return mCheckForLeavebehind; + } + /** * A listener that is notified when the empty space below the notifications is clicked on */ @@ -5935,7 +5822,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - void resetExposedMenuView(boolean animate, boolean force) { + private void resetExposedMenuView(boolean animate, boolean force) { mSwipeHelper.resetExposedMenuView(animate, force); } @@ -6293,6 +6180,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mKeyguardMediaControllorVisible = keyguardMediaControllorVisible; } + void resetCheckSnoozeLeavebehind() { + setCheckForLeaveBehind(true); + } + // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------ @ShadeViewRefactor(RefactorComponent.INPUT) @@ -6345,7 +6236,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @Override public void onTouchSlopExceeded() { cancelLongPress(); - checkSnoozeLeavebehind(); + mController.checkSnoozeLeavebehind(); } @Override 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 73b4cad4ced5..70892e0f9b38 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 @@ -34,6 +34,8 @@ import android.widget.FrameLayout; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.ExpandHelper; +import com.android.systemui.Gefingerpoken; import com.android.systemui.R; import com.android.systemui.SwipeHelper; import com.android.systemui.colorextraction.SysuiColorExtractor; @@ -58,7 +60,9 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; +import com.android.systemui.statusbar.notification.row.NotificationGuts; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.row.NotificationSnooze; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; @@ -70,6 +74,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; @@ -100,6 +105,7 @@ public class NotificationStackScrollLayoutController { private final NotificationSectionsManager mNotificationSectionsManager; private final Resources mResources; private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder; + private final ScrimController mScrimController; private final KeyguardMediaController mKeyguardMediaController; private final SysuiStatusBarStateController mStatusBarStateController; private final KeyguardBypassController mKeyguardBypassController; @@ -110,6 +116,7 @@ public class NotificationStackScrollLayoutController { private NotificationStackScrollLayout mView; private boolean mFadeNotificationsOnDismiss; + private NotificationSwipeHelper mSwipeHelper; private final NotificationListContainerImpl mNotificationListContainer = new NotificationListContainerImpl(); @@ -217,7 +224,16 @@ public class NotificationStackScrollLayoutController { @Override public void onMenuReset(View row) { - mView.onMenuReset(row); + View translatingParentView = mSwipeHelper.getTranslatingParentView(); + if (translatingParentView != null && row == translatingParentView) { + mSwipeHelper.clearExposedMenuView(); + mSwipeHelper.clearTranslatingParentView(); + if (row instanceof ExpandableNotificationRow) { + mHeadsUpManager.setMenuShown( + ((ExpandableNotificationRow) row).getEntry(), false); + + } + } } @Override @@ -228,7 +244,7 @@ public class NotificationStackScrollLayoutController { .setCategory(MetricsEvent.ACTION_REVEAL_GEAR) .setType(MetricsEvent.TYPE_ACTION)); mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true); - mView.onMenuShown(row); + mSwipeHelper.onMenuShown(row); mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */); @@ -246,7 +262,7 @@ public class NotificationStackScrollLayoutController { } // Close the menu row since we went directly to the guts - mView.resetExposedMenuView(false, true); + mSwipeHelper.resetExposedMenuView(false, true); } } } @@ -438,7 +454,31 @@ public class NotificationStackScrollLayoutController { } }; - private NotificationSwipeHelper mSwipeHelper; + private final OnHeadsUpChangedListener mOnHeadsUpChangedListener = + new OnHeadsUpChangedListener() { + @Override + public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { + mView.setInHeadsUpPinnedMode(inPinnedMode); + } + + @Override + public void onHeadsUpPinned(NotificationEntry entry) { + + } + + @Override + public void onHeadsUpUnPinned(NotificationEntry entry) { + + } + + @Override + public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { + long numEntries = mHeadsUpManager.getAllEntries().count(); + NotificationEntry topEntry = mHeadsUpManager.getTopEntry(); + mView.setNumHeadsUp(numEntries); + mView.setTopHeadsUpEntry(topEntry); + } + }; @Inject public NotificationStackScrollLayoutController( @@ -460,7 +500,8 @@ public class NotificationStackScrollLayoutController { NotificationSectionsManager notificationSectionsManager, @Main Resources resources, NotificationSwipeHelper.Builder notificationSwipeHelperBuilder, - StatusBar statusBar) { + StatusBar statusBar, + ScrimController scrimController) { mAllowLongPress = allowLongPress; mNotificationGutsManager = notificationGutsManager; mHeadsUpManager = headsUpManager; @@ -480,11 +521,13 @@ public class NotificationStackScrollLayoutController { mResources = resources; mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder; mStatusBar = statusBar; + mScrimController = scrimController; } public void attach(NotificationStackScrollLayout view) { mView = view; mView.setController(this); + mView.setTouchHandler(new TouchHandler()); mSwipeHelper = mNotificationSwipeHelperBuilder .setSwipeDirection(SwipeHelper.X) @@ -496,8 +539,12 @@ public class NotificationStackScrollLayoutController { mSwipeHelper); mHeadsUpManager.addListener(mNotificationRoundnessManager); // TODO: why is this here? + mHeadsUpManager.addListener(mOnHeadsUpChangedListener); + mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed); mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener); + mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming); + mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); mView.setCurrentUserid(mLockscreenUserManager.getCurrentUserId()); @@ -738,7 +785,12 @@ public class NotificationStackScrollLayoutController { } public void checkSnoozeLeavebehind() { - mView.checkSnoozeLeavebehind(); + if (mView.getCheckSnoozeLeaveBehind()) { + mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, + false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, + false /* resetMenu */); + mView.setCheckForLeaveBehind(false); + } } public void setQsExpanded(boolean expanded) { @@ -761,10 +813,6 @@ public class NotificationStackScrollLayoutController { mView.updateTopPadding(qsHeight, animate); } - public void resetCheckSnoozeLeavebehind() { - mView.resetCheckSnoozeLeavebehind(); - } - public boolean isScrolledToBottom() { return mView.isScrolledToBottom(); } @@ -815,9 +863,11 @@ public class NotificationStackScrollLayoutController { public void onExpansionStarted() { mView.onExpansionStarted(); + checkSnoozeLeavebehind(); } public void onExpansionStopped() { + mView.setCheckForLeaveBehind(false); mView.onExpansionStopped(); } @@ -910,7 +960,23 @@ public class NotificationStackScrollLayoutController { } public RemoteInputController.Delegate createDelegate() { - return mView.createDelegate(); + return new RemoteInputController.Delegate() { + public void setRemoteInputActive(NotificationEntry entry, + boolean remoteInputActive) { + mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive); + entry.notifyHeightChanged(true /* needsAnimation */); + updateFooter(); + } + + public void lockScrollTo(NotificationEntry entry) { + mView.lockScrollTo(entry.getRow()); + } + + public void requestDisallowLongPressAndDismiss() { + mView.requestDisallowLongPress(); + mView.requestDisallowDismiss(); + } + }; } public void updateSectionBoundaries(String reason) { @@ -958,18 +1024,10 @@ public class NotificationStackScrollLayoutController { mView.setShelfController(notificationShelfController); } - public void setScrimController(ScrimController scrimController) { - mView.setScrimController(scrimController); - } - public ExpandableView getFirstChildNotGone() { return mView.getFirstChildNotGone(); } - public void setInHeadsUpPinnedMode(boolean inPinnedMode) { - mView.setInHeadsUpPinnedMode(inPinnedMode); - } - public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) { mView.generateHeadsUpAnimation(entry, isHeadsUp); } @@ -1002,7 +1060,7 @@ public class NotificationStackScrollLayoutController { return mView.calculateGapHeight(previousView, child, count); } - public NotificationRoundnessManager getNoticationRoundessManager() { + NotificationRoundnessManager getNoticationRoundessManager() { return mNotificationRoundnessManager; } @@ -1010,6 +1068,32 @@ public class NotificationStackScrollLayoutController { return mNotificationListContainer; } + public void resetCheckSnoozeLeavebehind() { + mView.resetCheckSnoozeLeavebehind(); + } + + public void closeControlsIfOutsideTouch(MotionEvent ev) { + NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); + NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow(); + View translatingParentView = mSwipeHelper.getTranslatingParentView(); + View view = null; + if (guts != null && !guts.getGutsContent().isLeavebehind()) { + // Only close visible guts if they're not a leavebehind. + view = guts; + } else if (menuRow != null && menuRow.isMenuVisible() + && translatingParentView != null) { + // Checking menu + view = translatingParentView; + } + if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) { + // Touch was outside visible guts / menu notification, close what's visible + mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */, + false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */, + false /* resetMenu */); + mSwipeHelper.resetExposedMenuView(true /* animate */, true /* force */); + } + } + private class NotificationListContainerImpl implements NotificationListContainer { @Override public void setChildTransferInProgress(boolean childTransferInProgress) { @@ -1084,7 +1168,7 @@ public class NotificationStackScrollLayoutController { @Override public void resetExposedMenuView(boolean animate, boolean force) { - mView.resetExposedMenuView(animate, force); + mSwipeHelper.resetExposedMenuView(animate, force); } @Override @@ -1148,4 +1232,99 @@ public class NotificationStackScrollLayoutController { mView.setWillExpand(willExpand); } } + + class TouchHandler implements Gefingerpoken { + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + mView.initDownStates(ev); + mView.handleEmptySpaceClick(ev); + + NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); + boolean expandWantsIt = false; + boolean swipingInProgress = mView.isSwipingInProgress(); + if (!swipingInProgress && !mView.getOnlyScrollingInThisMotion() && guts == null) { + expandWantsIt = mView.getExpandHelper().onInterceptTouchEvent(ev); + } + boolean scrollWantsIt = false; + if (!swipingInProgress && !mView.isExpandingNotification()) { + scrollWantsIt = mView.onInterceptTouchEventScroll(ev); + } + boolean swipeWantsIt = false; + if (!mView.isBeingDragged() + && !mView.isExpandingNotification() + && !mView.getExpandedInThisMotion() + && !mView.getOnlyScrollingInThisMotion() + && !mView.getDisallowDismissInThisMotion()) { + swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev); + } + // Check if we need to clear any snooze leavebehinds + boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP; + if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt && + !expandWantsIt && !scrollWantsIt) { + mView.setCheckForLeaveBehind(false); + mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, + false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, + false /* resetMenu */); + } + if (ev.getActionMasked() == MotionEvent.ACTION_UP) { + mView.setCheckForLeaveBehind(true); + } + return swipeWantsIt || scrollWantsIt || expandWantsIt; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); + boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL + || ev.getActionMasked() == MotionEvent.ACTION_UP; + mView.handleEmptySpaceClick(ev); + boolean expandWantsIt = false; + boolean swipingInProgress = mView.getSwipingInProgress(); + boolean onlyScrollingInThisMotion = mView.getOnlyScrollingInThisMotion(); + boolean expandingNotification = mView.isExpandingNotification(); + if (mView.getIsExpanded() && !swipingInProgress && !onlyScrollingInThisMotion + && guts == null) { + ExpandHelper expandHelper = mView.getExpandHelper(); + if (isCancelOrUp) { + expandHelper.onlyObserveMovements(false); + } + boolean wasExpandingBefore = expandingNotification; + expandWantsIt = expandHelper.onTouchEvent(ev); + expandingNotification = mView.isExpandingNotification(); + if (mView.getExpandedInThisMotion() && !expandingNotification && wasExpandingBefore + && !mView.getDisallowScrollingInThisMotion()) { + mView.dispatchDownEventToScroller(ev); + } + } + boolean scrollerWantsIt = false; + if (mView.isExpanded() && !swipingInProgress && !expandingNotification + && !mView.getDisallowScrollingInThisMotion()) { + scrollerWantsIt = mView.onScrollTouch(ev); + } + boolean horizontalSwipeWantsIt = false; + if (!mView.isBeingDragged() + && !expandingNotification + && !mView.getExpandedInThisMotion() + && !onlyScrollingInThisMotion + && !mView.getDisallowDismissInThisMotion()) { + horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev); + } + + // Check if we need to clear any snooze leavebehinds + if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts) + && guts.getGutsContent() instanceof NotificationSnooze) { + NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent(); + if ((ns.isExpanded() && isCancelOrUp) + || (!horizontalSwipeWantsIt && scrollerWantsIt)) { + // If the leavebehind is expanded we clear it on the next up event, otherwise we + // clear it on the next non-horizontal swipe or expand event. + checkSnoozeLeavebehind(); + } + } + if (ev.getActionMasked() == MotionEvent.ACTION_UP) { + mView.setCheckForLeaveBehind(true); + } + return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index 1e80e88df1a1..ba01c8420ef2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -227,7 +227,7 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc || (isFastNonDismissGesture && isAbleToShowMenu); int menuSnapTarget = menuRow.getMenuSnapTarget(); boolean isNonFalseMenuRevealingGesture = - !isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu; + !isFalseGesture() && isMenuRevealingGestureAwayFromMenu; if ((isNonDismissGestureTowardsMenu || isNonFalseMenuRevealingGesture) && menuSnapTarget != 0) { // Menu has not been snapped to previously and this is menu revealing gesture diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java index 858023dc6c62..ba9420265849 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java @@ -27,6 +27,7 @@ import android.view.ViewConfiguration; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.classifier.Classifier; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.KeyguardAffordanceView; @@ -317,7 +318,9 @@ public class KeyguardAffordanceHelper { // We snap back if the current translation is not far enough boolean snapBack = false; if (mCallback.needsAntiFalsing()) { - snapBack = snapBack || mFalsingManager.isFalseTouch(); + snapBack = snapBack || mFalsingManager.isFalseTouch( + mTargetedView == mRightIcon + ? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE); } snapBack = snapBack || isBelowFalsingThreshold(); 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 d83758a57401..1cd85e3b3cc1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import static android.view.View.GONE; +import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; @@ -68,9 +69,11 @@ import com.android.keyguard.KeyguardClockSwitchController; import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.DejankUtils; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.classifier.Classifier; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeLog; @@ -129,7 +132,6 @@ 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 { @@ -260,7 +262,7 @@ public class NotificationPanelViewController extends PanelViewController { private final ConversationNotificationManager mConversationNotificationManager; private final MediaHierarchyManager mMediaHierarchyManager; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - private final Provider<KeyguardClockSwitchController> mKeyguardClockSwitchControllerProvider; + private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card. // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications private final int mMaxKeyguardNotifications; @@ -511,9 +513,9 @@ public class NotificationPanelViewController extends PanelViewController { MediaHierarchyManager mediaHierarchyManager, BiometricUnlockController biometricUnlockController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, - Provider<KeyguardClockSwitchController> keyguardClockSwitchControllerProvider, NotificationStackScrollLayoutController notificationStackScrollLayoutController, - NotificationIconAreaController notificationIconAreaController) { + NotificationIconAreaController notificationIconAreaController, + KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) { super(view, falsingManager, dozeLog, keyguardStateController, (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager); @@ -525,9 +527,9 @@ public class NotificationPanelViewController extends PanelViewController { mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder; mMediaHierarchyManager = mediaHierarchyManager; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; - mKeyguardClockSwitchControllerProvider = keyguardClockSwitchControllerProvider; mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; mNotificationIconAreaController = notificationIconAreaController; + mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; mView.setWillNotDraw(!DEBUG); mInjectionInflationController = injectionInflationController; mFalsingManager = falsingManager; @@ -602,8 +604,10 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardStatusView = mView.findViewById(R.id.keyguard_status_view); KeyguardClockSwitchController keyguardClockSwitchController = - mKeyguardClockSwitchControllerProvider.get(); - keyguardClockSwitchController.attach(mView.findViewById(R.id.keyguard_clock_container)); + mKeyguardStatusViewComponentFactory + .build(mKeyguardStatusView) + .getKeyguardClockSwitchController(); + keyguardClockSwitchController.init(); mBigClockContainer = mView.findViewById(R.id.big_clock_container); keyguardClockSwitchController.setBigClockContainer(mBigClockContainer); @@ -733,8 +737,10 @@ public class NotificationPanelViewController extends PanelViewController { // Re-associate the clock container with the keyguard clock switch. mBigClockContainer.removeAllViews(); KeyguardClockSwitchController keyguardClockSwitchController = - mKeyguardClockSwitchControllerProvider.get(); - keyguardClockSwitchController.attach(mView.findViewById(R.id.keyguard_clock_container)); + mKeyguardStatusViewComponentFactory + .build(mKeyguardStatusView) + .getKeyguardClockSwitchController(); + keyguardClockSwitchController.init(); keyguardClockSwitchController.setBigClockContainer(mBigClockContainer); // Update keyguard bottom area @@ -1264,7 +1270,7 @@ public class NotificationPanelViewController extends PanelViewController { } private boolean flingExpandsQs(float vel) { - if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) { + if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) { return false; } if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { @@ -1274,12 +1280,12 @@ public class NotificationPanelViewController extends PanelViewController { } } - private boolean isFalseTouch() { + private boolean isFalseTouch(@Classifier.InteractionType int interactionType) { if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) { return false; } if (mFalsingManager.isClassifierEnabled()) { - return mFalsingManager.isFalseTouch(); + return mFalsingManager.isFalseTouch(interactionType); } return !mQsTouchAboveFalsingThreshold; } @@ -3066,9 +3072,6 @@ public class NotificationPanelViewController extends PanelViewController { if (mKeyguardStatusBar != null) { mKeyguardStatusBar.dump(fd, pw, args); } - if (mKeyguardStatusView != null) { - mKeyguardStatusView.dump(fd, pw, args); - } } public boolean hasActiveClearableNotifications() { @@ -3137,15 +3140,13 @@ public class NotificationPanelViewController extends PanelViewController { public void initDependencies( StatusBar statusBar, NotificationGroupManager groupManager, - NotificationShelfController notificationShelfController, - ScrimController scrimController) { + NotificationShelfController notificationShelfController) { setStatusBar(statusBar); setGroupManager(mGroupManager); mNotificationStackScrollLayoutController.setNotificationPanelController(this); mNotificationStackScrollLayoutController.setStatusBar(statusBar); mNotificationStackScrollLayoutController.setGroupManager(groupManager); mNotificationStackScrollLayoutController.setShelfController(notificationShelfController); - mNotificationStackScrollLayoutController.setScrimController(scrimController); updateShowEmptyShadeView(); mNotificationShelfController = notificationShelfController; updateMaxDisplayedNotifications(true); @@ -3543,7 +3544,6 @@ public class NotificationPanelViewController extends PanelViewController { private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener { @Override public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) { - mNotificationStackScrollLayoutController.setInHeadsUpPinnedMode(inPinnedMode); if (inPinnedMode) { mHeadsUpExistenceChangedRunnable.run(); updateNotificationTranslucency(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java index 53cc2676723c..a3d3c2bb0af5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java @@ -52,6 +52,7 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.InjectionInflationController; @@ -83,6 +84,7 @@ public class NotificationShadeWindowViewController { private final NotificationShadeWindowView mView; private final ShadeController mShadeController; private final NotificationShadeDepthController mDepthController; + private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; private GestureDetector mGestureDetector; private View mBrightnessMirror; @@ -130,7 +132,8 @@ public class NotificationShadeWindowViewController { NotificationShadeDepthController depthController, NotificationShadeWindowView notificationShadeWindowView, NotificationPanelViewController notificationPanelViewController, - SuperStatusBarViewFactory statusBarViewFactory) { + SuperStatusBarViewFactory statusBarViewFactory, + NotificationStackScrollLayoutController notificationStackScrollLayoutController) { mInjectionInflationController = injectionInflationController; mCoordinator = coordinator; mPulseExpansionHandler = pulseExpansionHandler; @@ -152,6 +155,7 @@ public class NotificationShadeWindowViewController { mNotificationPanelViewController = notificationPanelViewController; mDepthController = depthController; mStatusBarViewFactory = statusBarViewFactory; + mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; // This view is not part of the newly inflated expanded status bar. mBrightnessMirror = mView.findViewById(R.id.brightness_mirror); @@ -245,7 +249,7 @@ public class NotificationShadeWindowViewController { } } if (isDown) { - mStackScrollLayout.closeControlsIfOutsideTouch(ev); + mNotificationStackScrollLayoutController.closeControlsIfOutsideTouch(ev); } if (mStatusBarStateController.isDozing()) { mService.mDozeScrimController.extendPulse(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 965368e78e69..0e72506c6d94 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -16,6 +16,10 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; +import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; +import static com.android.systemui.classifier.Classifier.UNLOCK; + import static java.lang.Float.isNaN; import android.animation.Animator; @@ -41,6 +45,7 @@ import com.android.internal.util.LatencyTracker; import com.android.systemui.DejankUtils; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.classifier.Classifier; import com.android.systemui.doze.DozeLog; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.FlingAnimationUtils; @@ -397,7 +402,12 @@ public abstract class PanelViewController { mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp); mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK); } - fling(vel, expand, isFalseTouch(x, y)); + @Classifier.InteractionType int interactionType = vel > 0 + ? QUICK_SETTINGS : ( + mKeyguardStateController.canDismissLockScreen() + ? UNLOCK : BOUNCER_UNLOCK); + + fling(vel, expand, isFalseTouch(x, y, interactionType)); onTrackingStopped(expand); mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown; if (mUpdateFlingOnLayout) { @@ -492,7 +502,11 @@ public abstract class PanelViewController { return true; } - if (isFalseTouch(x, y)) { + @Classifier.InteractionType int interactionType = vel > 0 + ? QUICK_SETTINGS : ( + mKeyguardStateController.canDismissLockScreen() ? UNLOCK : BOUNCER_UNLOCK); + + if (isFalseTouch(x, y, interactionType)) { return true; } if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { @@ -511,12 +525,13 @@ public abstract class PanelViewController { * @param y the final y-coordinate when the finger was lifted * @return whether this motion should be regarded as a false touch */ - private boolean isFalseTouch(float x, float y) { + private boolean isFalseTouch(float x, float y, + @Classifier.InteractionType int interactionType) { if (!mStatusBar.isFalsingThresholdNeeded()) { return false; } if (mFalsingManager.isClassifierEnabled()) { - return mFalsingManager.isFalseTouch(); + return mFalsingManager.isFalseTouch(interactionType); } if (!mTouchAboveFalsingThreshold) { return true; 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 11d05830d065..e95cf2806691 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -141,6 +141,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private ScrimView mScrimBehind; private ScrimView mScrimForBubble; + private Runnable mScrimBehindChangeRunnable; + private final KeyguardStateController mKeyguardStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final DozeParameters mDozeParameters; @@ -241,6 +243,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo mScrimInFront = scrimInFront; mScrimForBubble = scrimForBubble; + if (mScrimBehindChangeRunnable != null) { + mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable); + mScrimBehindChangeRunnable = null; + } + final ScrimState[] states = ScrimState.values(); for (int i = 0; i < states.length; i++) { states[i].init(mScrimInFront, mScrimBehind, mScrimForBubble, mDozeParameters, @@ -934,7 +941,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } public void setScrimBehindChangeRunnable(Runnable changeRunnable) { - mScrimBehind.setChangeRunnable(changeRunnable); + // TODO: remove this. This is necessary because of an order-of-operations limitation. + // The fix is to move more of these class into @StatusBarScope + if (mScrimBehind == null) { + mScrimBehindChangeRunnable = changeRunnable; + } else { + mScrimBehind.setChangeRunnable(changeRunnable); + } } public void setCurrentUser(int currentUser) { 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 115d164bd761..58828797cfde 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1160,8 +1160,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanelViewController.initDependencies( this, mGroupManager, - mNotificationShelfController, - mScrimController); + mNotificationShelfController); BackDropView backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop); mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java new file mode 100644 index 000000000000..914105fdc4c4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy.dagger; + +import com.android.systemui.statusbar.policy.BluetoothController; +import com.android.systemui.statusbar.policy.BluetoothControllerImpl; +import com.android.systemui.statusbar.policy.CastController; +import com.android.systemui.statusbar.policy.CastControllerImpl; +import com.android.systemui.statusbar.policy.ExtensionController; +import com.android.systemui.statusbar.policy.ExtensionControllerImpl; +import com.android.systemui.statusbar.policy.FlashlightController; +import com.android.systemui.statusbar.policy.FlashlightControllerImpl; +import com.android.systemui.statusbar.policy.HotspotController; +import com.android.systemui.statusbar.policy.HotspotControllerImpl; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl; +import com.android.systemui.statusbar.policy.LocationController; +import com.android.systemui.statusbar.policy.LocationControllerImpl; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkControllerImpl; +import com.android.systemui.statusbar.policy.NextAlarmController; +import com.android.systemui.statusbar.policy.NextAlarmControllerImpl; +import com.android.systemui.statusbar.policy.RotationLockController; +import com.android.systemui.statusbar.policy.RotationLockControllerImpl; +import com.android.systemui.statusbar.policy.SecurityController; +import com.android.systemui.statusbar.policy.SecurityControllerImpl; +import com.android.systemui.statusbar.policy.SensorPrivacyController; +import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl; +import com.android.systemui.statusbar.policy.UserInfoController; +import com.android.systemui.statusbar.policy.UserInfoControllerImpl; +import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.statusbar.policy.ZenModeControllerImpl; + +import dagger.Binds; +import dagger.Module; + + +/** Dagger Module for code in the statusbar.policy package. */ +@Module +public interface StatusBarPolicyModule { + /** */ + @Binds + BluetoothController provideBluetoothController(BluetoothControllerImpl controllerImpl); + + /** */ + @Binds + CastController provideCastController(CastControllerImpl controllerImpl); + + /** */ + @Binds + ExtensionController provideExtensionController(ExtensionControllerImpl controllerImpl); + + /** */ + @Binds + FlashlightController provideFlashlightController(FlashlightControllerImpl controllerImpl); + + /** */ + @Binds + KeyguardStateController provideKeyguardMonitor(KeyguardStateControllerImpl controllerImpl); + + /** */ + @Binds + HotspotController provideHotspotController(HotspotControllerImpl controllerImpl); + + /** */ + @Binds + LocationController provideLocationController(LocationControllerImpl controllerImpl); + + /** */ + @Binds + NetworkController provideNetworkController(NetworkControllerImpl controllerImpl); + + /** */ + @Binds + NextAlarmController provideNextAlarmController(NextAlarmControllerImpl controllerImpl); + + /** */ + @Binds + RotationLockController provideRotationLockController(RotationLockControllerImpl controllerImpl); + + /** */ + @Binds + SecurityController provideSecurityController(SecurityControllerImpl controllerImpl); + + /** */ + @Binds + SensorPrivacyController provideSensorPrivacyControllerImpl( + SensorPrivacyControllerImpl controllerImpl); + + /** */ + @Binds + UserInfoController provideUserInfoContrller(UserInfoControllerImpl controllerImpl); + + /** */ + @Binds + ZenModeController provideZenModeController(ZenModeControllerImpl controllerImpl); + +} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/dagger/TunerModule.java b/packages/SystemUI/src/com/android/systemui/tuner/dagger/TunerModule.java new file mode 100644 index 000000000000..faf7b842f3b2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/dagger/TunerModule.java @@ -0,0 +1,31 @@ +/* + * 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.tuner.dagger; + +import com.android.systemui.tuner.TunerService; +import com.android.systemui.tuner.TunerServiceImpl; + +import dagger.Binds; +import dagger.Module; + +/** Dagger Module for code in the tuner package. */ +@Module +public interface TunerModule { + /** */ + @Binds + TunerService provideTunerService(TunerServiceImpl controllerImpl); +} diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java index 37aac1124048..df741a0e98ff 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java @@ -16,7 +16,9 @@ package com.android.systemui.tv; +import com.android.systemui.dagger.GlobalModule; import com.android.systemui.dagger.GlobalRootComponent; +import com.android.systemui.dagger.WMModule; import javax.inject.Singleton; @@ -26,7 +28,11 @@ import dagger.Component; * Root component for Dagger injection. */ @Singleton -@Component(modules = {TvSysUIComponentModule.class}) +@Component(modules = { + GlobalModule.class, + TvSysUIComponentModule.class, + WMModule.class +}) public interface TvGlobalRootComponent extends GlobalRootComponent { /** * Component Builder interface. This allows to bind Context instance in the component diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java index 302301d79c3a..bef05ebb724e 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java @@ -17,11 +17,9 @@ package com.android.systemui.tv; import com.android.systemui.dagger.DefaultComponentBinder; -import com.android.systemui.dagger.DependencyBinder; import com.android.systemui.dagger.DependencyProvider; import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.SystemServicesModule; import com.android.systemui.dagger.SystemUIBinder; import com.android.systemui.dagger.SystemUIModule; @@ -34,8 +32,6 @@ import dagger.Subcomponent; @Subcomponent(modules = { DefaultComponentBinder.class, DependencyProvider.class, - DependencyBinder.class, - SystemServicesModule.class, SystemUIBinder.class, SystemUIModule.class, TvSystemUIModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index d278905abacb..eb8f065149c8 100644 --- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java +++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java @@ -24,7 +24,6 @@ import android.view.LayoutInflater; import android.view.View; import com.android.keyguard.KeyguardMessageArea; -import com.android.keyguard.KeyguardSliceView; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.qs.QSFooterImpl; import com.android.systemui.qs.QSPanel; @@ -109,11 +108,6 @@ public class InjectionInflationController { NotificationStackScrollLayout createNotificationStackScrollLayout(); /** - * Creates the KeyguardSliceView. - */ - KeyguardSliceView createKeyguardSliceView(); - - /** * Creates the KeyguardMessageArea. */ KeyguardMessageArea createKeyguardMessageArea(); diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java new file mode 100644 index 000000000000..5946af383b0f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java @@ -0,0 +1,70 @@ +/* + * 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.util.concurrency; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; + +import com.android.systemui.dagger.qualifiers.Main; + +import java.util.concurrent.Executor; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; + +/** + * Dagger Module for classes found within the concurrent package. + */ +@Module +public abstract class GlobalConcurrencyModule { + + /** + * Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}. + */ + @Binds + public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl); + + /** Main Looper */ + @Provides + @Main + public static Looper provideMainLooper() { + return Looper.getMainLooper(); + } + + /** + * Main Handler. + * + * Prefer the Main Executor when possible. + */ + @Provides + @Main + public static Handler provideMainHandler(@Main Looper mainLooper) { + return new Handler(mainLooper); + } + + /** + * Provide a Main-Thread Executor. + */ + @Provides + @Main + public static Executor provideMainExecutor(Context context) { + return context.getMainExecutor(); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java index 628c808aa12b..b9b20c73c5d5 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java @@ -16,7 +16,6 @@ package com.android.systemui.util.concurrency; -import android.content.Context; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -31,7 +30,6 @@ import com.android.systemui.dagger.qualifiers.UiBackground; import java.util.concurrent.Executor; import java.util.concurrent.Executors; -import dagger.Binds; import dagger.Module; import dagger.Provides; @@ -39,7 +37,7 @@ import dagger.Provides; * Dagger Module for classes found within the concurrent package. */ @Module -public abstract class ConcurrencyModule { +public abstract class SysUIConcurrencyModule { /** Background Looper */ @Provides @SysUISingleton @@ -62,13 +60,6 @@ public abstract class ConcurrencyModule { return thread.getLooper(); } - /** Main Looper */ - @Provides - @Main - public static Looper provideMainLooper() { - return Looper.getMainLooper(); - } - /** * Background Handler. * @@ -81,17 +72,6 @@ public abstract class ConcurrencyModule { } /** - * Main Handler. - * - * Prefer the Main Executor when possible. - */ - @Provides - @Main - public static Handler provideMainHandler(@Main Looper mainLooper) { - return new Handler(mainLooper); - } - - /** * Provide a Background-Thread Executor by default. */ @Provides @@ -121,15 +101,6 @@ public abstract class ConcurrencyModule { } /** - * Provide a Main-Thread Executor. - */ - @Provides - @Main - public static Executor provideMainExecutor(Context context) { - return context.getMainExecutor(); - } - - /** * Provide a Background-Thread Executor by default. */ @Provides @@ -199,10 +170,4 @@ public abstract class ConcurrencyModule { public static Executor provideUiBackgroundExecutor() { return Executors.newSingleThreadExecutor(); } - - /** - * Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}. - */ - @Binds - public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl); } diff --git a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java new file mode 100644 index 000000000000..cdfa1457f4a5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java @@ -0,0 +1,31 @@ +/* + * 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.util.dagger; + +import com.android.systemui.util.RingerModeTracker; +import com.android.systemui.util.RingerModeTrackerImpl; + +import dagger.Binds; +import dagger.Module; + +/** Dagger Module for code in the util package. */ +@Module +public interface UtilModule { + /** */ + @Binds + RingerModeTracker provideRingerModeTracker(RingerModeTrackerImpl ringerModeTrackerImpl); +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 51ad30ebcac6..78f83d3c09b4 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -49,6 +49,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.PixelFormat; +import android.graphics.Region; import android.graphics.drawable.ColorDrawable; import android.media.AudioManager; import android.media.AudioSystem; @@ -71,6 +72,7 @@ import android.view.View.AccessibilityDelegate; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; import android.view.ViewStub; +import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; @@ -109,7 +111,8 @@ import java.util.List; * Methods ending in "H" must be called on the (ui) handler. */ public class VolumeDialogImpl implements VolumeDialog, - ConfigurationController.ConfigurationListener { + ConfigurationController.ConfigurationListener, + ViewTreeObserver.OnComputeInternalInsetsListener { private static final String TAG = Util.logTag(VolumeDialogImpl.class); private static final long USER_ATTEMPT_GRACE_PERIOD = 1000; @@ -126,6 +129,7 @@ public class VolumeDialogImpl implements VolumeDialog, private final H mHandler = new H(); private final VolumeDialogController mController; private final DeviceProvisionedController mDeviceProvisionedController; + private final Region mTouchableRegion = new Region(); private Window mWindow; private CustomDialog mDialog; @@ -204,6 +208,33 @@ public class VolumeDialogImpl implements VolumeDialog, Dependency.get(ConfigurationController.class).removeCallback(this); } + @Override + public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo internalInsetsInfo) { + // Set touchable region insets on the root dialog view. This tells WindowManager that + // touches outside of this region should not be delivered to the volume window, and instead + // go to the window below. This is the only way to do this - returning false in + // onDispatchTouchEvent results in the event being ignored entirely, rather than passed to + // the next window. + internalInsetsInfo.setTouchableInsets( + ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); + + mTouchableRegion.setEmpty(); + + // Set the touchable region to the union of all child view bounds. We don't use touches on + // the volume dialog container itself, so this is fine. + for (int i = 0; i < mDialogView.getChildCount(); i++) { + final View view = mDialogView.getChildAt(i); + mTouchableRegion.op( + view.getLeft(), + view.getTop(), + view.getRight(), + view.getBottom(), + Region.Op.UNION); + } + + internalInsetsInfo.touchableRegion.set(mTouchableRegion); + } + private void initDialog() { mDialog = new CustomDialog(mContext); @@ -235,6 +266,7 @@ public class VolumeDialogImpl implements VolumeDialog, mDialogView.setAlpha(0); mDialog.setCanceledOnTouchOutside(true); mDialog.setOnShowListener(dialog -> { + mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this); if (!isLandscape()) mDialogView.setTranslationX(mDialogView.getWidth() / 2.0f); mDialogView.setAlpha(0); mDialogView.animate() @@ -253,6 +285,11 @@ public class VolumeDialogImpl implements VolumeDialog, .start(); }); + mDialog.setOnDismissListener(dialogInterface -> + mDialogView + .getViewTreeObserver() + .removeOnComputeInternalInsetsListener(VolumeDialogImpl.this)); + mDialogView.setOnHoverListener((v, event) -> { int action = event.getActionMasked(); mHovering = (action == MotionEvent.ACTION_HOVER_ENTER) @@ -1369,6 +1406,11 @@ public class VolumeDialogImpl implements VolumeDialog, super(context, R.style.volume_dialog_theme); } + /** + * NOTE: This will only be called for touches within the touchable region of the volume + * dialog, as returned by {@link #onComputeInternalInsets}. Other touches, even if they are + * within the bounds of the volume dialog, will fall through to the window below. + */ @Override public boolean dispatchTouchEvent(MotionEvent ev) { rescheduleTimeoutH(); @@ -1387,6 +1429,12 @@ public class VolumeDialogImpl implements VolumeDialog, mHandler.sendEmptyMessage(H.RECHECK_ALL); } + /** + * NOTE: This will be called with ACTION_OUTSIDE MotionEvents for touches that occur outside + * of the touchable region of the volume dialog (as returned by + * {@link #onComputeInternalInsets}) even if those touches occurred within the bounds of the + * volume dialog. + */ @Override public boolean onTouchEvent(MotionEvent event) { if (mShowing) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java new file mode 100644 index 000000000000..1ef4c169b786 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -0,0 +1,32 @@ +/* + * 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.volume.dagger; + +import com.android.systemui.volume.VolumeComponent; +import com.android.systemui.volume.VolumeDialogComponent; + +import dagger.Binds; +import dagger.Module; + + +/** Dagger Module for code in the volume package. */ +@Module +public interface VolumeModule { + /** */ + @Binds + VolumeComponent provideVolumeComponent(VolumeDialogComponent volumeDialogComponent); +} diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 49226000e97d..e4ff1b5dcf4f 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -29,7 +29,11 @@ import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.stackdivider.SplitScreen; import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.protolog.ShellProtoLogImpl; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Arrays; import java.util.Optional; import javax.inject.Inject; @@ -108,4 +112,35 @@ public final class WMShell extends SystemUI { } }); } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + // Handle commands if provided + for (int i = 0; i < args.length; i++) { + switch (args[i]) { + case "enable-text-logging": { + String[] groups = Arrays.copyOfRange(args, i + 1, args.length); + startTextLogging(groups); + pw.println("Starting logging on groups: " + Arrays.toString(groups)); + return; + } + case "disable-text-logging": { + String[] groups = Arrays.copyOfRange(args, i + 1, args.length); + stopTextLogging(groups); + pw.println("Stopping logging on groups: " + Arrays.toString(groups)); + return; + } + } + } + + // Dump WMShell stuff here if no commands were handled + } + + private void startTextLogging(String... groups) { + ShellProtoLogImpl.getSingleInstance().startTextLogging(mContext, groups); + } + + private void stopTextLogging(String... groups) { + ShellProtoLogImpl.getSingleInstance().stopTextLogging(groups); + } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 7b4547689c8b..6a2ca44bfc17 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -22,8 +22,6 @@ import android.view.IWindowManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.pip.phone.PipMenuActivity; -import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; import com.android.systemui.stackdivider.SplitScreen; import com.android.systemui.stackdivider.SplitScreenController; import com.android.wm.shell.ShellTaskOrganizer; @@ -50,14 +48,6 @@ public class WMShellModule { return new DisplayImeController(wmService, displayController, mainHandler, transactionPool); } - /** TODO(b/150319024): PipMenuActivity will move to a Window */ - @SysUISingleton - @PipMenuActivityClass - @Provides - static Class<?> providePipMenuActivityClass() { - return PipMenuActivity.class; - } - @SysUISingleton @Provides static SplitScreen provideSplitScreen(Context context, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 657e4fbb4633..3aa6ec08683f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -62,6 +62,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { private ClockPlugin mClockPlugin; @Mock ColorExtractor.GradientColors mGradientColors; + @Mock + KeyguardSliceViewController mKeyguardSliceViewController; private KeyguardClockSwitchController mController; @@ -69,28 +71,30 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { public void setup() { MockitoAnnotations.initMocks(this); + when(mView.isAttachedToWindow()).thenReturn(true); + mController = new KeyguardClockSwitchController( - mStatusBarStateController, mColorExtractor, mClockManager); + mView, mStatusBarStateController, mColorExtractor, mClockManager, + mKeyguardSliceViewController); - when(mView.isAttachedToWindow()).thenReturn(true); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors); } @Test - public void testAttach_viewAlreadyAttached() { - mController.attach(mView); + public void testInit_viewAlreadyAttached() { + mController.init(); verifyAttachment(times(1)); } @Test - public void testAttach_viewNotYetAttached() { + public void testInit_viewNotYetAttached() { ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); when(mView.isAttachedToWindow()).thenReturn(false); - mController.attach(mView); + mController.init(); verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture()); verifyAttachment(never()); @@ -100,12 +104,17 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { verifyAttachment(times(1)); } + @Test + public void testInitSubControllers() { + mController.init(); + verify(mKeyguardSliceViewController).init(); + } @Test - public void testAttach_viewDetached() { + public void testInit_viewDetached() { ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); - mController.attach(mView); + mController.init(); verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture()); verifyAttachment(times(1)); @@ -122,7 +131,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { public void testBigClockPassesStatusBarState() { ViewGroup testView = new FrameLayout(mContext); - mController.attach(mView); + mController.init(); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); mController.setBigClockContainer(testView); verify(mView).setBigClockContainer(testView, StatusBarState.SHADE); @@ -143,7 +152,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { ArgumentCaptor<ClockManager.ClockChangedListener> listenerArgumentCaptor = ArgumentCaptor.forClass(ClockManager.ClockChangedListener.class); - mController.attach(mView); + mController.init(); verify(mClockManager).addOnClockChangedListener(listenerArgumentCaptor.capture()); listenerArgumentCaptor.getValue().onClockChanged(mClockPlugin); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java index 446b1228f1bb..559284ac0672 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import android.content.Context; @@ -28,6 +29,7 @@ import android.view.View; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardDisplayManager.KeyguardPresentation; +import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; @@ -51,6 +53,12 @@ public class KeyguardPresentationTest extends SysuiTestCase { KeyguardSliceView mMockKeyguardSliceView; @Mock KeyguardStatusView mMockKeyguardStatusView; + @Mock + private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; + @Mock + private KeyguardStatusViewComponent mKeyguardStatusViewComponent; + @Mock + private KeyguardClockSwitchController mKeyguardClockSwitchController; LayoutInflater mLayoutInflater; @@ -62,6 +70,11 @@ public class KeyguardPresentationTest extends SysuiTestCase { when(mMockKeyguardSliceView.getContext()).thenReturn(mContext); when(mMockKeyguardStatusView.getContext()).thenReturn(mContext); when(mMockKeyguardStatusView.findViewById(R.id.clock)).thenReturn(mMockKeyguardStatusView); + when(mKeyguardStatusViewComponentFactory.build(any(KeyguardStatusView.class))) + .thenReturn(mKeyguardStatusViewComponent); + when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) + .thenReturn(mKeyguardClockSwitchController); + allowTestableLooperAsMainThread(); InjectionInflationController inflationController = new InjectionInflationController( @@ -99,7 +112,8 @@ public class KeyguardPresentationTest extends SysuiTestCase { @Test public void testInflation_doesntCrash() { KeyguardPresentation keyguardPresentation = new KeyguardPresentation(mContext, - mContext.getDisplayNoVerify(), mLayoutInflater); + mContext.getDisplayNoVerify(), mKeyguardStatusViewComponentFactory, + mLayoutInflater); keyguardPresentation.onCreate(null /*savedInstanceState */); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java new file mode 100644 index 000000000000..b7bcaa3c3566 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.View; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.keyguard.KeyguardSliceProvider; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.tuner.TunerService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper(setAsMainLooper = true) +public class KeyguardSliceViewControllerTest extends SysuiTestCase { + @Mock + private KeyguardSliceView mView;; + @Mock + private KeyguardStatusView mKeyguardStatusView; + @Mock + private TunerService mTunerService; + @Mock + private ConfigurationController mConfigurationController; + @Mock + private ActivityStarter mActivityStarter; + private DumpManager mDumpManager = new DumpManager(); + + private KeyguardSliceViewController mController; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mView.isAttachedToWindow()).thenReturn(true); + when(mView.getContext()).thenReturn(mContext); + mController = new KeyguardSliceViewController( + mView, mKeyguardStatusView, mActivityStarter, mConfigurationController, + mTunerService, mDumpManager); + mController.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI); + } + + @Test + public void refresh_replacesSliceContentAndNotifiesListener() { + mController.refresh(); + verify(mView).hideSlice(); + } + + @Test + public void onAttachedToWindow_registersListeners() { + mController.init(); + verify(mTunerService).addTunable(any(TunerService.Tunable.class), anyString()); + verify(mConfigurationController).addCallback( + any(ConfigurationController.ConfigurationListener.class)); + } + + @Test + public void onDetachedFromWindow_unregistersListeners() { + ArgumentCaptor<View.OnAttachStateChangeListener> attachListenerArgumentCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); + + mController.init(); + verify(mView).addOnAttachStateChangeListener(attachListenerArgumentCaptor.capture()); + + attachListenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView); + + verify(mTunerService).removeTunable(any(TunerService.Tunable.class)); + verify(mConfigurationController).removeCallback( + any(ConfigurationController.ConfigurationListener.class)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java index 06552b9bdbb4..1ab08c27088a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java @@ -15,37 +15,27 @@ */ package com.android.keyguard; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.content.res.Resources; import android.graphics.Color; import android.net.Uri; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; -import android.util.AttributeSet; import android.view.LayoutInflater; -import android.view.View; +import androidx.slice.Slice; import androidx.slice.SliceProvider; import androidx.slice.SliceSpecs; import androidx.slice.builders.ListBuilder; +import androidx.slice.widget.RowContent; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.KeyguardSliceProvider; -import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.tuner.TunerService; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.Collections; @@ -53,46 +43,18 @@ import java.util.HashSet; import java.util.concurrent.atomic.AtomicBoolean; @SmallTest -@RunWithLooper +@RunWithLooper(setAsMainLooper = true) @RunWith(AndroidTestingRunner.class) public class KeyguardSliceViewTest extends SysuiTestCase { private KeyguardSliceView mKeyguardSliceView; private Uri mSliceUri; - @Mock - private TunerService mTunerService; - @Mock - private ConfigurationController mConfigurationController; - @Mock - private ActivityStarter mActivityStarter; - @Mock - private Resources mResources; - @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - allowTestableLooperAsMainThread(); LayoutInflater layoutInflater = LayoutInflater.from(getContext()); - layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() { - - @Override - public View onCreateView(View parent, String name, Context context, - AttributeSet attrs) { - return onCreateView(name, context, attrs); - } - - @Override - public View onCreateView(String name, Context context, AttributeSet attrs) { - if ("com.android.keyguard.KeyguardSliceView".equals(name)) { - return new KeyguardSliceView(getContext(), attrs, mActivityStarter, - mConfigurationController, mTunerService, mResources); - } - return null; - } - }); mKeyguardSliceView = (KeyguardSliceView) layoutInflater .inflate(R.layout.keyguard_status_area, null); - mKeyguardSliceView.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI); mSliceUri = Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI); SliceProvider.setSpecs(new HashSet<>(Collections.singletonList(SliceSpecs.LIST))); } @@ -100,9 +62,13 @@ public class KeyguardSliceViewTest extends SysuiTestCase { @Test public void showSlice_notifiesListener() { ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY); + builder.setHeader(new ListBuilder.HeaderBuilder().setTitle("header title!")); + Slice slice = builder.build(); + RowContent rowContent = new RowContent(slice.getItemArray()[0], 0); + AtomicBoolean notified = new AtomicBoolean(); mKeyguardSliceView.setContentChangeListener(()-> notified.set(true)); - mKeyguardSliceView.onChanged(builder.build()); + mKeyguardSliceView.showSlice(rowContent, Collections.EMPTY_LIST); Assert.assertTrue("Listener should be notified about slice changes.", notified.get()); } @@ -111,7 +77,7 @@ public class KeyguardSliceViewTest extends SysuiTestCase { public void showSlice_emptySliceNotifiesListener() { AtomicBoolean notified = new AtomicBoolean(); mKeyguardSliceView.setContentChangeListener(()-> notified.set(true)); - mKeyguardSliceView.onChanged(null); + mKeyguardSliceView.showSlice(null, Collections.EMPTY_LIST); Assert.assertTrue("Listener should be notified about slice changes.", notified.get()); } @@ -119,24 +85,17 @@ public class KeyguardSliceViewTest extends SysuiTestCase { @Test public void hasHeader_readsSliceData() { ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY); - mKeyguardSliceView.onChanged(builder.build()); + mKeyguardSliceView.showSlice(null, Collections.EMPTY_LIST); Assert.assertFalse("View should not have a header", mKeyguardSliceView.hasHeader()); builder.setHeader(new ListBuilder.HeaderBuilder().setTitle("header title!")); - mKeyguardSliceView.onChanged(builder.build()); + Slice slice = builder.build(); + RowContent rowContent = new RowContent(slice.getItemArray()[0], 0); + mKeyguardSliceView.showSlice(rowContent, Collections.EMPTY_LIST); Assert.assertTrue("View should have a header", mKeyguardSliceView.hasHeader()); } @Test - public void refresh_replacesSliceContentAndNotifiesListener() { - AtomicBoolean notified = new AtomicBoolean(); - mKeyguardSliceView.setContentChangeListener(()-> notified.set(true)); - mKeyguardSliceView.refresh(); - Assert.assertTrue("Listener should be notified about slice changes.", - notified.get()); - } - - @Test public void getTextColor_whiteTextWhenAOD() { // Set text color to red since the default is white and test would always pass mKeyguardSliceView.setTextColor(Color.RED); @@ -147,18 +106,4 @@ public class KeyguardSliceViewTest extends SysuiTestCase { Assert.assertEquals("Should be using AOD text color", Color.WHITE, mKeyguardSliceView.getTextColor()); } - - @Test - public void onAttachedToWindow_registersListeners() { - mKeyguardSliceView.onAttachedToWindow(); - verify(mTunerService).addTunable(eq(mKeyguardSliceView), anyString()); - verify(mConfigurationController).addCallback(eq(mKeyguardSliceView)); - } - - @Test - public void onDetachedFromWindow_unregistersListeners() { - mKeyguardSliceView.onDetachedFromWindow(); - verify(mTunerService).removeTunable(eq(mKeyguardSliceView)); - verify(mConfigurationController).removeCallback(eq(mKeyguardSliceView)); - } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java index 0bf137689aa1..0431704778c3 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java @@ -40,7 +40,8 @@ import org.mockito.Mock; public class KeyguardStatusViewTest extends SysuiTestCase { @Mock - KeyguardSliceView mKeyguardSlice; + KeyguardSliceViewController mKeyguardSliceViewController; + @Mock KeyguardClockSwitch mClockView; @InjectMocks @@ -64,7 +65,7 @@ public class KeyguardStatusViewTest extends SysuiTestCase { @Test public void dozeTimeTick_updatesSlice() { mKeyguardStatusView.dozeTimeTick(); - verify(mKeyguardSlice).refresh(); + verify(mKeyguardSliceViewController).refresh(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index c8e0f490a783..909acead9553 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -69,7 +69,7 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.GlobalActions; import com.android.systemui.plugins.GlobalActionsPanelPlugin; -import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -125,7 +125,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase { @Mock GlobalActionsPanelPlugin mWalletPlugin; @Mock GlobalActionsPanelPlugin.PanelViewController mWalletController; @Mock private Handler mHandler; - @Mock private CurrentUserContextTracker mCurrentUserContextTracker; + @Mock private UserContextProvider mUserContextProvider; private ControlsComponent mControlsComponent; private TestableLooper mTestableLooper; @@ -137,7 +137,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase { allowTestableLooperAsMainThread(); when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData); - when(mCurrentUserContextTracker.getCurrentUserContext()).thenReturn(mContext); + when(mUserContextProvider.getUserContext()).thenReturn(mContext); mControlsComponent = new ControlsComponent( true, () -> mControlsController, @@ -176,7 +176,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase { mSysUiState, mHandler, mControlsComponent, - mCurrentUserContextTracker + mUserContextProvider ); mGlobalActionsDialog.setZeroDialogPressDelayForTesting(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index c874b1fb72ad..f6d6f562e3fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -37,6 +37,7 @@ import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardDisplayManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; @@ -46,7 +47,6 @@ import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.DeviceConfigProxyFake; -import com.android.systemui.util.InjectionInflationController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -72,7 +72,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock PowerManager mPowerManager; private @Mock TrustManager mTrustManager; private @Mock NavigationModeController mNavigationModeController; - private @Mock InjectionInflationController mInjectionInflationController; + private @Mock KeyguardDisplayManager mKeyguardDisplayManager; private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -91,7 +91,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { () -> mStatusBarKeyguardViewManager, mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor, mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController, - mInjectionInflationController); + mKeyguardDisplayManager); mViewMediator.start(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index 4c9e141c45cc..3e37fde84544 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -33,7 +33,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import org.junit.Before; @@ -62,7 +62,7 @@ public class RecordingServiceTest extends SysuiTestCase { @Mock private Executor mExecutor; @Mock - private CurrentUserContextTracker mUserContextTracker; + private UserContextProvider mUserContextTracker; private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil() { public void executeWhenUnlocked(ActivityStarter.OnDismissAction action, boolean requiresShadeOpen) { @@ -92,7 +92,7 @@ public class RecordingServiceTest extends SysuiTestCase { doNothing().when(mRecordingService).startForeground(anyInt(), any()); doReturn(mScreenMediaRecorder).when(mRecordingService).getRecorder(); - doReturn(mContext).when(mUserContextTracker).getCurrentUserContext(); + doReturn(mContext).when(mUserContextTracker).getUserContext(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt deleted file mode 100644 index 628c06a56abd..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.settings - -import android.content.Context -import android.content.ContextWrapper -import android.os.UserHandle -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.broadcast.BroadcastDispatcher -import junit.framework.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.`when` -import org.mockito.Mockito.mock -import org.mockito.MockitoAnnotations - -@SmallTest -@RunWith(AndroidTestingRunner::class) -@TestableLooper.RunWithLooper -class CurrentUserContextTrackerTest : SysuiTestCase() { - - private lateinit var tracker: CurrentUserContextTracker - @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - allowTestableLooperAsMainThread() - - // wrap Context so that tests don't throw for missing package errors - val wrapped = object : ContextWrapper(context) { - override fun createContextAsUser(user: UserHandle, flags: Int): Context { - val mockContext = mock(Context::class.java) - `when`(mockContext.user).thenReturn(user) - `when`(mockContext.userId).thenReturn(user.identifier) - return mockContext - } - } - - tracker = CurrentUserContextTracker(wrapped, broadcastDispatcher) - tracker.initialize() - } - - @Test - fun testContextExistsAfterInit_noCrash() { - tracker.currentUserContext - } - - @Test - fun testUserContextIsCorrectAfterUserSwitch() { - // We always start out with system ui test - assertTrue("Starting userId should be 0", tracker.currentUserContext.userId == 0) - - // WHEN user changes - tracker.handleUserSwitched(1) - - // THEN user context should have the correct userId - assertTrue("User has changed to userId 1, the context should reflect that", - tracker.currentUserContext.userId == 1) - } - - @Suppress("UNUSED_PARAMETER") - @Test(expected = IllegalStateException::class) - fun testContextTrackerThrowsExceptionWhenNotInitialized() { - // GIVEN an uninitialized CurrentUserContextTracker - val userTracker = CurrentUserContextTracker(context, broadcastDispatcher) - - // WHEN client asks for a context - val userContext = userTracker.currentUserContext - - // THEN an exception is thrown - } -}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt new file mode 100644 index 000000000000..f76b50a173c3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt @@ -0,0 +1,292 @@ +/* + * 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.settings + +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.UserInfo +import android.os.Handler +import android.os.UserHandle +import android.os.UserManager +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.DumpManager +import com.android.systemui.util.mockito.capture +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.eq +import org.mockito.ArgumentMatchers.isNull +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import java.util.concurrent.Executor + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class UserTrackerImplTest : SysuiTestCase() { + + @Mock + private lateinit var context: Context + @Mock + private lateinit var userManager: UserManager + @Mock(stubOnly = true) + private lateinit var dumpManager: DumpManager + @Mock(stubOnly = true) + private lateinit var handler: Handler + + private val executor = Executor(Runnable::run) + private lateinit var tracker: UserTrackerImpl + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + `when`(context.userId).thenReturn(UserHandle.USER_SYSTEM) + `when`(context.user).thenReturn(UserHandle.SYSTEM) + `when`(context.createContextAsUser(any(), anyInt())).thenAnswer { invocation -> + val user = invocation.getArgument<UserHandle>(0) + `when`(context.user).thenReturn(user) + `when`(context.userId).thenReturn(user.identifier) + context + } + `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation -> + val info = UserInfo(invocation.getArgument<Int>(0), "", UserInfo.FLAG_FULL) + listOf(info) + } + + tracker = UserTrackerImpl(context, userManager, dumpManager, handler) + } + + @Test + fun testNotInitialized() { + assertThat(tracker.initialized).isFalse() + } + + @Test(expected = IllegalStateException::class) + fun testGetUserIdBeforeInitThrowsException() { + tracker.userId + } + + @Test(expected = IllegalStateException::class) + fun testGetUserHandleBeforeInitThrowsException() { + tracker.userHandle + } + + @Test(expected = IllegalStateException::class) + fun testGetUserContextBeforeInitThrowsException() { + tracker.userContext + } + + @Test(expected = IllegalStateException::class) + fun testGetUserContentResolverBeforeInitThrowsException() { + tracker.userContentResolver + } + + @Test(expected = IllegalStateException::class) + fun testGetUserProfilesBeforeInitThrowsException() { + tracker.userProfiles + } + + @Test + fun testInitialize() { + tracker.initialize(0) + + assertThat(tracker.initialized).isTrue() + } + + @Test + fun testReceiverRegisteredOnInitialize() { + tracker.initialize(0) + + val captor = ArgumentCaptor.forClass(IntentFilter::class.java) + + verify(context).registerReceiverForAllUsers( + eq(tracker), capture(captor), isNull(), eq(handler)) + } + + @Test + fun testInitialValuesSet() { + val testID = 4 + tracker.initialize(testID) + + verify(userManager).getProfiles(testID) + + assertThat(tracker.userId).isEqualTo(testID) + assertThat(tracker.userHandle).isEqualTo(UserHandle.of(testID)) + assertThat(tracker.userContext.userId).isEqualTo(testID) + assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(testID)) + assertThat(tracker.userProfiles).hasSize(1) + + val info = tracker.userProfiles[0] + assertThat(info.id).isEqualTo(testID) + } + + @Test + fun testUserSwitch() { + tracker.initialize(0) + val newID = 5 + + val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID) + tracker.onReceive(context, intent) + + verify(userManager).getProfiles(newID) + + assertThat(tracker.userId).isEqualTo(newID) + assertThat(tracker.userHandle).isEqualTo(UserHandle.of(newID)) + assertThat(tracker.userContext.userId).isEqualTo(newID) + assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(newID)) + assertThat(tracker.userProfiles).hasSize(1) + + val info = tracker.userProfiles[0] + assertThat(info.id).isEqualTo(newID) + } + + @Test + fun testManagedProfileAvailable() { + tracker.initialize(0) + val profileID = tracker.userId + 10 + + `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation -> + val id = invocation.getArgument<Int>(0) + val info = UserInfo(id, "", UserInfo.FLAG_FULL) + val infoProfile = UserInfo( + id + 10, + "", + "", + UserInfo.FLAG_MANAGED_PROFILE, + UserManager.USER_TYPE_PROFILE_MANAGED + ) + infoProfile.profileGroupId = id + listOf(info, infoProfile) + } + + val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) + .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID)) + tracker.onReceive(context, intent) + + assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID) + } + + @Test + fun testCallbackNotCalledOnAdd() { + tracker.initialize(0) + val callback = TestCallback() + + tracker.addCallback(callback, executor) + + assertThat(callback.calledOnProfilesChanged).isEqualTo(0) + assertThat(callback.calledOnUserChanged).isEqualTo(0) + } + + @Test + fun testCallbackCalledOnUserChanged() { + tracker.initialize(0) + val callback = TestCallback() + tracker.addCallback(callback, executor) + + val newID = 5 + + val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID) + tracker.onReceive(context, intent) + + assertThat(callback.calledOnUserChanged).isEqualTo(1) + assertThat(callback.lastUser).isEqualTo(newID) + assertThat(callback.lastUserContext?.userId).isEqualTo(newID) + assertThat(callback.calledOnProfilesChanged).isEqualTo(1) + assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(newID) + } + + @Test + fun testCallbackCalledOnProfileChanged() { + tracker.initialize(0) + val callback = TestCallback() + tracker.addCallback(callback, executor) + val profileID = tracker.userId + 10 + + `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation -> + val id = invocation.getArgument<Int>(0) + val info = UserInfo(id, "", UserInfo.FLAG_FULL) + val infoProfile = UserInfo( + id + 10, + "", + "", + UserInfo.FLAG_MANAGED_PROFILE, + UserManager.USER_TYPE_PROFILE_MANAGED + ) + infoProfile.profileGroupId = id + listOf(info, infoProfile) + } + + val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) + .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID)) + + tracker.onReceive(context, intent) + + assertThat(callback.calledOnUserChanged).isEqualTo(0) + assertThat(callback.calledOnProfilesChanged).isEqualTo(1) + assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID) + } + + @Test + fun testCallbackRemoved() { + tracker.initialize(0) + val newID = 5 + val profileID = newID + 10 + + val callback = TestCallback() + tracker.addCallback(callback, executor) + tracker.removeCallback(callback) + + val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, 5) + tracker.onReceive(context, intent) + + val intentProfiles = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) + .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID)) + + tracker.onReceive(context, intentProfiles) + + assertThat(callback.calledOnUserChanged).isEqualTo(0) + assertThat(callback.calledOnProfilesChanged).isEqualTo(0) + } + + private class TestCallback : UserTracker.Callback { + var calledOnUserChanged = 0 + var calledOnProfilesChanged = 0 + var lastUser: Int? = null + var lastUserContext: Context? = null + var lastUserProfiles = emptyList<UserInfo>() + + override fun onUserChanged(newUser: Int, userContext: Context) { + calledOnUserChanged++ + lastUser = newUser + lastUserContext = userContext + } + + override fun onProfilesChanged(profiles: List<UserInfo>) { + calledOnProfilesChanged++ + lastUserProfiles = profiles + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index c2c40cac3d0f..e1668cab3333 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -69,7 +69,7 @@ import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.SysuiTestCase; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; -import com.android.systemui.settings.CurrentUserContextTracker; +import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.notification.AssistantFeedbackController; @@ -128,7 +128,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Mock private ShortcutManager mShortcutManager; @Mock private ChannelEditorDialogController mChannelEditorDialogController; @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier; - @Mock private CurrentUserContextTracker mContextTracker; + @Mock private UserContextProvider mContextTracker; @Mock private BubbleController mBubbleController; @Mock(answer = Answers.RETURNS_SELF) private PriorityOnboardingDialogController.Builder mBuilder; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index fa9ea6b1ea10..62b741c1938a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -78,12 +78,10 @@ import com.android.systemui.statusbar.notification.people.PeopleNotificationIden import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; -import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationIconAreaController; -import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.leak.LeakDetector; @@ -122,7 +120,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private EmptyShadeView mEmptyShadeView; @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private RemoteInputController mRemoteInputController; - @Mock private NotificationIconAreaController mNotificationIconAreaController; @Mock private MetricsLogger mMetricsLogger; @Mock private NotificationRoundnessManager mNotificationRoundnessManager; @Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider; @@ -203,9 +200,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mNotificationRoundnessManager, mock(DynamicPrivacyController.class), mStatusBarStateController, - mHeadsUpManager, - new FalsingManagerFake(), - mock(NotificationGutsManager.class), mNotificationSectionsManager, mock(ForegroundServiceSectionController.class), mock(ForegroundServiceDismissalFeatureController.class), @@ -220,7 +214,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mStackScroller = spy(mStackScrollerInternal); mStackScroller.setShelfController(notificationShelfController); mStackScroller.setStatusBar(mBar); - mStackScroller.setScrimController(mock(ScrimController.class)); mStackScroller.setGroupManager(mGroupManager); mStackScroller.setEmptyShadeView(mEmptyShadeView); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java index 4fffd3e0124a..f5d9fa07fa1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java @@ -49,6 +49,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.LockscreenGestureLogger; +import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -110,6 +112,8 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { private NotificationSwipeHelper mNotificationSwipeHelper; @Mock private StatusBar mStatusBar; + @Mock + private ScrimController mScrimController; private NotificationStackScrollLayoutController mController; @@ -138,7 +142,8 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { mNotificationSectionsManager, mResources, mNotificationSwipeHelperBuilder, - mStatusBar + mStatusBar, + mScrimController ); when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true); 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 04e870d4b35c..453baa5e16fd 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 @@ -54,6 +54,7 @@ import com.android.keyguard.KeyguardClockSwitch; import com.android.keyguard.KeyguardClockSwitchController; import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; @@ -111,8 +112,6 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private ViewGroup mBigClockContainer; @Mock - private ScrimController mScrimController; - @Mock private NotificationIconAreaController mNotificationAreaController; @Mock private HeadsUpManagerPhone mHeadsUpManager; @@ -187,10 +186,16 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Mock + private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; + @Mock + private KeyguardStatusViewComponent mKeyguardStatusViewComponent; + @Mock private KeyguardClockSwitchController mKeyguardClockSwitchController; @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; + private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; + private NotificationPanelViewController mNotificationPanelViewController; private View.AccessibilityDelegate mAccessibiltyDelegate; @@ -241,6 +246,10 @@ public class NotificationPanelViewTest extends SysuiTestCase { mock(NotificationRoundnessManager.class), mStatusBarStateController, new FalsingManagerFake()); + when(mKeyguardStatusViewComponentFactory.build(any())) + .thenReturn(mKeyguardStatusViewComponent); + when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) + .thenReturn(mKeyguardClockSwitchController); mNotificationPanelViewController = new NotificationPanelViewController(mView, mResources, mInjectionInflationController, @@ -254,14 +263,13 @@ public class NotificationPanelViewTest extends SysuiTestCase { flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, mConversationNotificationManager, mMediaHiearchyManager, mBiometricUnlockController, mStatusBarKeyguardViewManager, - () -> mKeyguardClockSwitchController, mNotificationStackScrollLayoutController, - mNotificationAreaController); + mNotificationAreaController, + mKeyguardStatusViewComponentFactory); mNotificationPanelViewController.initDependencies( mStatusBar, mGroupManager, - mNotificationShelfController, - mScrimController); + mNotificationShelfController); mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); mNotificationPanelViewController.setBar(mPanelBar); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java index c1d51f3beb7d..25af584ed3f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java @@ -46,6 +46,7 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.InjectionInflationController; @@ -85,6 +86,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private NotificationShadeDepthController mNotificationShadeDepthController; @Mock private SuperStatusBarViewFactory mStatusBarViewFactory; @Mock private NotificationShadeWindowController mNotificationShadeWindowController; + @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; @Before public void setUp() { @@ -123,11 +125,11 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { mNotificationShadeDepthController, mView, mNotificationPanelViewController, - mStatusBarViewFactory); + mStatusBarViewFactory, + mNotificationStackScrollLayoutController); mController.setupExpandedStatusBar(); mController.setService(mStatusBar, mNotificationShadeWindowController); mController.setDragDownHelper(mDragDownHelper); - } @Test diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java index 070626be9f80..c70dfcc93e49 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java @@ -256,6 +256,7 @@ class EventDispatcher { return actionMasked; } } + /** * Sends down events to the view hierarchy for all pointers which are not already being * delivered i.e. pointers that are not yet injected. @@ -285,6 +286,74 @@ class EventDispatcher { } /** + * Sends down events to the view hierarchy for all pointers which are not already being + * delivered with original down location. i.e. pointers that are not yet injected. + * + * @param prototype The prototype from which to create the injected events. + * @param policyFlags The policy flags associated with the event. + */ + void sendDownForAllNotInjectedPointersWithOriginalDown(MotionEvent prototype, int policyFlags) { + // Inject the injected pointers. + int pointerIdBits = 0; + final int pointerCount = prototype.getPointerCount(); + final MotionEvent event = computeEventWithOriginalDown(prototype); + for (int i = 0; i < pointerCount; i++) { + final int pointerId = prototype.getPointerId(i); + // Do not send event for already delivered pointers. + if (!mState.isInjectedPointerDown(pointerId)) { + pointerIdBits |= (1 << pointerId); + final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i); + sendMotionEvent( + event, + action, + mState.getLastReceivedEvent(), + pointerIdBits, + policyFlags); + } + } + } + + private MotionEvent computeEventWithOriginalDown(MotionEvent prototype) { + final int pointerCount = prototype.getPointerCount(); + if (pointerCount != mState.getReceivedPointerTracker().getReceivedPointerDownCount()) { + Slog.w(LOG_TAG, "The pointer count doesn't match the received count."); + return MotionEvent.obtain(prototype); + } + MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[pointerCount]; + MotionEvent.PointerProperties[] properties = + new MotionEvent.PointerProperties[pointerCount]; + for (int i = 0; i < pointerCount; ++i) { + final int pointerId = prototype.getPointerId(i); + final float x = mState.getReceivedPointerTracker().getReceivedPointerDownX(pointerId); + final float y = mState.getReceivedPointerTracker().getReceivedPointerDownY(pointerId); + coords[i] = new MotionEvent.PointerCoords(); + coords[i].x = x; + coords[i].y = y; + properties[i] = new MotionEvent.PointerProperties(); + properties[i].id = pointerId; + properties[i].toolType = MotionEvent.TOOL_TYPE_FINGER; + } + MotionEvent event = + MotionEvent.obtain( + prototype.getDownTime(), + prototype.getEventTime(), + prototype.getAction(), + pointerCount, + properties, + coords, + prototype.getMetaState(), + prototype.getButtonState(), + prototype.getXPrecision(), + prototype.getYPrecision(), + prototype.getDeviceId(), + prototype.getEdgeFlags(), + prototype.getSource(), + prototype.getFlags()); + return event; + } + + /** + * * Sends up events to the view hierarchy for all pointers which are already being delivered i.e. * pointers that are injected. * diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java index 07e82111d4e5..5b46cb4ab378 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java @@ -294,7 +294,7 @@ class MultiFingerSwipe extends GestureMatcher { + Float.toString(mGestureDetectionThresholdPixels)); } if (getState() == STATE_CLEAR) { - if (moveDelta < mTouchSlop) { + if (moveDelta < (mTargetFingerCount * mTouchSlop)) { // This still counts as a touch not a swipe. continue; } else if (mStrokeBuffers[pointerIndex].size() == 0) { diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index b587dd33c4e5..8305be393ab1 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -594,15 +594,23 @@ public class TouchExplorer extends BaseEventStreamTransformation if (pointerIndex < 0) { return; } - final float deltaX = - mReceivedPointerTracker.getReceivedPointerDownX(pointerId) - - rawEvent.getX(pointerIndex); - final float deltaY = - mReceivedPointerTracker.getReceivedPointerDownY(pointerId) - - rawEvent.getY(pointerIndex); - final double moveDelta = Math.hypot(deltaX, deltaY); - if (moveDelta < mTouchSlop) { - return; + // Require both fingers to have moved a certain amount before starting a drag. + for (int index = 0; index < event.getPointerCount(); ++index) { + int id = event.getPointerId(index); + if (!mReceivedPointerTracker.isReceivedPointerDown(id)) { + // Something is wrong with the event stream. + Slog.e(LOG_TAG, "Invalid pointer id: " + id); + } + final float deltaX = + mReceivedPointerTracker.getReceivedPointerDownX(id) + - rawEvent.getX(index); + final float deltaY = + mReceivedPointerTracker.getReceivedPointerDownY(id) + - rawEvent.getY(index); + final double moveDelta = Math.hypot(deltaX, deltaY); + if (moveDelta < (2 * mTouchSlop)) { + return; + } } } // More than one pointer so the user is not touch exploring @@ -612,12 +620,20 @@ public class TouchExplorer extends BaseEventStreamTransformation if (isDraggingGesture(event)) { // Two pointers moving in the same direction within // a given distance perform a drag. - mState.startDragging(); computeDraggingPointerIdIfNeeded(event); pointerIdBits = 1 << mDraggingPointerId; event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags()); - mDispatcher.sendMotionEvent( - event, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags); + MotionEvent downEvent = computeDownEventForDrag(event); + if (downEvent != null) { + mDispatcher.sendMotionEvent( + downEvent, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags); + mDispatcher.sendMotionEvent( + event, ACTION_MOVE, rawEvent, pointerIdBits, policyFlags); + } else { + mDispatcher.sendMotionEvent( + event, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags); + } + mState.startDragging(); } else { // Two pointers moving arbitrary are delegated to the view hierarchy. mState.startDelegating(); @@ -628,23 +644,20 @@ public class TouchExplorer extends BaseEventStreamTransformation if (mGestureDetector.isMultiFingerGesturesEnabled()) { if (mGestureDetector.isTwoFingerPassthroughEnabled()) { if (event.getPointerCount() == 3) { - boolean isOnBottomEdge = true; // If three fingers went down on the bottom edge of the screen, delegate // immediately. - final long screenHeight = - mContext.getResources().getDisplayMetrics().heightPixels; - for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) { - if (mReceivedPointerTracker.getReceivedPointerDownY(i) - < (screenHeight - mEdgeSwipeHeightPixels)) { - isOnBottomEdge = false; - } - } - if (isOnBottomEdge) { + if (allPointersDownOnBottomEdge(event)) { if (DEBUG) { Slog.d(LOG_TAG, "Three-finger edge swipe detected."); } mState.startDelegating(); - mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); + if (mState.isTouchExploring()) { + mDispatcher.sendDownForAllNotInjectedPointers(event, + policyFlags); + } else { + mDispatcher.sendDownForAllNotInjectedPointersWithOriginalDown( + event, policyFlags); + } } } } @@ -1004,6 +1017,65 @@ public class TouchExplorer extends BaseEventStreamTransformation return distance; } + /** + * Creates a down event using the down coordinates of the dragging pointer and other information + * from the supplied event. The supplied event's down time is adjusted to reflect the time when + * the dragging pointer initially went down. + */ + private MotionEvent computeDownEventForDrag(MotionEvent event) { + // Creating a down event only makes sense if we haven't started touch exploring yet. + if (mState.isTouchExploring() + || mDraggingPointerId == INVALID_POINTER_ID + || event == null) { + return null; + } + final float x = mReceivedPointerTracker.getReceivedPointerDownX(mDraggingPointerId); + final float y = mReceivedPointerTracker.getReceivedPointerDownY(mDraggingPointerId); + final long time = mReceivedPointerTracker.getReceivedPointerDownTime(mDraggingPointerId); + MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[1]; + coords[0] = new MotionEvent.PointerCoords(); + coords[0].x = x; + coords[0].y = y; + MotionEvent.PointerProperties[] properties = new MotionEvent.PointerProperties[1]; + properties[0] = new MotionEvent.PointerProperties(); + properties[0].id = mDraggingPointerId; + properties[0].toolType = MotionEvent.TOOL_TYPE_FINGER; + MotionEvent downEvent = + MotionEvent.obtain( + time, + time, + ACTION_DOWN, + 1, + properties, + coords, + event.getMetaState(), + event.getButtonState(), + event.getXPrecision(), + event.getYPrecision(), + event.getDeviceId(), + event.getEdgeFlags(), + event.getSource(), + event.getFlags()); + event.setDownTime(time); + return downEvent; + } + + private boolean allPointersDownOnBottomEdge(MotionEvent event) { + final long screenHeight = + mContext.getResources().getDisplayMetrics().heightPixels; + for (int i = 0; i < event.getPointerCount(); ++i) { + final int pointerId = event.getPointerId(i); + final float pointerDownY = mReceivedPointerTracker.getReceivedPointerDownY(pointerId); + if (pointerDownY < (screenHeight - mEdgeSwipeHeightPixels)) { + if (DEBUG) { + Slog.d(LOG_TAG, "The pointer is not on the bottom edge" + pointerDownY); + } + return false; + } + } + return true; + } + public TouchState getState() { return mState; } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java index 3ee5b28ee338..7d6067c8e1da 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java @@ -176,7 +176,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl Slog.i(LOG_TAG, "onDestroy(); delayed = " + mDetectingState.toString()); } - mWindowMagnificationMgr.disableWindowMagnifier(mDisplayId, true); + mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true); resetToDetectState(); } @@ -211,14 +211,14 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl final float scale = MathUtils.constrain( mWindowMagnificationMgr.getPersistedScale(), MIN_SCALE, MAX_SCALE); - mWindowMagnificationMgr.enableWindowMagnifier(mDisplayId, scale, centerX, centerY); + mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY); } private void disableWindowMagnifier() { if (DEBUG_ALL) { Slog.i(LOG_TAG, "disableWindowMagnifier()"); } - mWindowMagnificationMgr.disableWindowMagnifier(mDisplayId, false); + mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, false); } private void toggleMagnification(float centerX, float centerY) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java index ed2b26f24478..ecbece6f1f27 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -73,7 +73,7 @@ public class WindowMagnificationManager implements public void onReceive(Context context, Intent intent) { final int displayId = context.getDisplayId(); removeMagnificationButton(displayId); - disableWindowMagnification(displayId); + disableWindowMagnification(displayId, false); } }; @@ -136,10 +136,10 @@ public class WindowMagnificationManager implements /** * Requests {@link IWindowMagnificationConnection} through * {@link StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)} and - * destroys all window magnifiers if necessary. + * destroys all window magnifications if necessary. * * @param connect {@code true} if needs connection, otherwise set the connection to null and - * destroy all window magnifiers. + * destroy all window magnifications. * @return {@code true} if {@link IWindowMagnificationConnection} state is going to change. */ public boolean requestConnection(boolean connect) { @@ -171,7 +171,7 @@ public class WindowMagnificationManager implements private void disableAllWindowMagnifiers() { for (int i = 0; i < mWindowMagnifiers.size(); i++) { final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i); - magnifier.disable(); + magnifier.disableWindowMagnificationInternal(); } mWindowMagnifiers.clear(); } @@ -187,12 +187,12 @@ public class WindowMagnificationManager implements @Override public boolean processScroll(int displayId, float distanceX, float distanceY) { - moveWindowMagnifier(displayId, -distanceX, -distanceY); + moveWindowMagnification(displayId, -distanceX, -distanceY); return /* event consumed: */ true; } /** - * Scales the magnified region on the specified display if the window magnifier is enabled. + * Scales the magnified region on the specified display if the window magnifier is initiated. * * @param displayId The logical display id. * @param scale The target scale, must be >= 1 @@ -209,7 +209,7 @@ public class WindowMagnificationManager implements } /** - * Enables the window magnifier with specified center and scale on the specified display. + * Enables window magnification with specified center and scale on the specified display. * @param displayId The logical display id. * @param scale The target scale, must be >= 1. * @param centerX The screen-relative X coordinate around which to center, @@ -217,29 +217,29 @@ public class WindowMagnificationManager implements * @param centerY The screen-relative Y coordinate around which to center, * or {@link Float#NaN} to leave unchanged. */ - void enableWindowMagnifier(int displayId, float scale, float centerX, float centerY) { + void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) { synchronized (mLock) { WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); if (magnifier == null) { magnifier = createWindowMagnifier(displayId); } - magnifier.enable(scale, centerX, centerY); + magnifier.enableWindowMagnificationInternal(scale, centerX, centerY); } } /** - * Disables the window magnifier on the specified display. + * Disables window magnification on the specified display. * * @param displayId The logical display id. * @param clear {@true} Clears the state of the window magnifier */ - void disableWindowMagnifier(int displayId, boolean clear) { + void disableWindowMagnification(int displayId, boolean clear) { synchronized (mLock) { WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); if (magnifier == null) { return; } - magnifier.disable(); + magnifier.disableWindowMagnificationInternal(); if (clear) { mWindowMagnifiers.delete(displayId); } @@ -264,10 +264,10 @@ public class WindowMagnificationManager implements } /** - * Indicates whether this window magnifier is enabled on specified display. + * Indicates whether window magnification is enabled on specified display. * * @param displayId The logical display id. - * @return {@code true} if the window magnifier is enabled. + * @return {@code true} if the window magnification is enabled. */ boolean isWindowMagnifierEnabled(int displayId) { synchronized (mLock) { @@ -323,7 +323,7 @@ public class WindowMagnificationManager implements } /** - * Moves the window magnifier with specified offset. + * Moves window magnification on the specified display with the specified offset. * * @param displayId The logical display id. * @param offsetX the amount in pixels to offset the region in the X direction, in current @@ -331,7 +331,7 @@ public class WindowMagnificationManager implements * @param offsetY the amount in pixels to offset the region in the Y direction, in current * screen pixels. */ - void moveWindowMagnifier(int displayId, float offsetX, float offsetY) { + void moveWindowMagnification(int displayId, float offsetX, float offsetY) { synchronized (mLock) { WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); if (magnifier == null) { @@ -425,7 +425,8 @@ public class WindowMagnificationManager implements } /** - * A class to manipulate the window magnifier and contains the relevant information. + * A class manipulates window magnification per display and contains the magnification + * information. */ private static class WindowMagnifier { @@ -434,7 +435,7 @@ public class WindowMagnificationManager implements private boolean mEnabled; private final WindowMagnificationManager mWindowMagnificationManager; - //Records the bounds of window magnifier. + //Records the bounds of window magnification. private final Rect mBounds = new Rect(); //The magnified bounds on the screen. private final Rect mSourceBounds = new Rect(); @@ -444,12 +445,12 @@ public class WindowMagnificationManager implements } @GuardedBy("mLock") - void enable(float scale, float centerX, float centerY) { + void enableWindowMagnificationInternal(float scale, float centerX, float centerY) { if (mEnabled) { return; } final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); - if (mWindowMagnificationManager.enableWindowMagnification(mDisplayId, normScale, + if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale, centerX, centerY)) { mScale = normScale; mEnabled = true; @@ -457,8 +458,9 @@ public class WindowMagnificationManager implements } @GuardedBy("mLock") - void disable() { - if (mEnabled && mWindowMagnificationManager.disableWindowMagnification(mDisplayId)) { + void disableWindowMagnificationInternal() { + if (mEnabled && mWindowMagnificationManager.disableWindowMagnificationInternal( + mDisplayId)) { mEnabled = false; } } @@ -519,7 +521,7 @@ public class WindowMagnificationManager implements } } - private boolean enableWindowMagnification(int displayId, float scale, float centerX, + private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX, float centerY) { return mConnectionWrapper != null && mConnectionWrapper.enableWindowMagnification( displayId, scale, centerX, centerY); @@ -529,7 +531,7 @@ public class WindowMagnificationManager implements return mConnectionWrapper != null && mConnectionWrapper.setScale(displayId, scale); } - private boolean disableWindowMagnification(int displayId) { + private boolean disableWindowMagnificationInternal(int displayId) { return mConnectionWrapper != null && mConnectionWrapper.disableWindowMagnification( displayId); } diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java index 1c4db1214d3b..59ba82e4616a 100644 --- a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java +++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java @@ -34,6 +34,7 @@ import android.app.prediction.IPredictionManager; import android.content.Context; import android.content.pm.ParceledListSlice; import android.os.Binder; +import android.os.IBinder; import android.os.ResultReceiver; import android.os.ShellCallback; import android.util.Slog; @@ -108,9 +109,9 @@ public class AppPredictionManagerService extends @Override public void createPredictionSession(@NonNull AppPredictionContext context, - @NonNull AppPredictionSessionId sessionId) { - runForUserLocked("createPredictionSession", sessionId, - (service) -> service.onCreatePredictionSessionLocked(context, sessionId)); + @NonNull AppPredictionSessionId sessionId, @NonNull IBinder token) { + runForUserLocked("createPredictionSession", sessionId, (service) -> + service.onCreatePredictionSessionLocked(context, sessionId, token)); } @Override diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java index 7ee607c3eab4..735f420ca03e 100644 --- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java +++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java @@ -30,6 +30,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.ServiceInfo; +import android.os.IBinder; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.provider.DeviceConfig; @@ -44,8 +45,6 @@ import com.android.server.LocalServices; import com.android.server.infra.AbstractPerUserSystemService; import com.android.server.people.PeopleServiceInternal; -import java.util.function.Consumer; - /** * Per-user instance of {@link AppPredictionManagerService}. */ @@ -112,17 +111,24 @@ public class AppPredictionPerUserService extends */ @GuardedBy("mLock") public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context, - @NonNull AppPredictionSessionId sessionId) { - if (!mSessionInfos.containsKey(sessionId)) { - mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context, - DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, - PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false), - this::removeAppPredictionSessionInfo)); - } - final boolean serviceExists = resolveService(sessionId, s -> - s.onCreatePredictionSession(context, sessionId), true); - if (!serviceExists) { - mSessionInfos.remove(sessionId); + @NonNull AppPredictionSessionId sessionId, @NonNull IBinder token) { + final boolean usesPeopleService = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, + PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false); + final boolean serviceExists = resolveService(sessionId, false, + usesPeopleService, s -> s.onCreatePredictionSession(context, sessionId)); + if (serviceExists && !mSessionInfos.containsKey(sessionId)) { + final AppPredictionSessionInfo sessionInfo = new AppPredictionSessionInfo( + sessionId, context, usesPeopleService, token, () -> { + synchronized (mLock) { + onDestroyPredictionSessionLocked(sessionId); + } + }); + if (sessionInfo.linkToDeath()) { + mSessionInfos.put(sessionId, sessionInfo); + } else { + // destroy the session if calling process is already dead + onDestroyPredictionSessionLocked(sessionId); + } } } @@ -132,7 +138,10 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId, @NonNull AppTargetEvent event) { - resolveService(sessionId, s -> s.notifyAppTargetEvent(sessionId, event), false); + final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); + if (sessionInfo == null) return; + resolveService(sessionId, false, sessionInfo.mUsesPeopleService, + s -> s.notifyAppTargetEvent(sessionId, event)); } /** @@ -141,8 +150,10 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId, @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) { - resolveService(sessionId, s -> - s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds), false); + final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); + if (sessionInfo == null) return; + resolveService(sessionId, false, sessionInfo.mUsesPeopleService, + s -> s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds)); } /** @@ -151,7 +162,10 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId, @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) { - resolveService(sessionId, s -> s.sortAppTargets(sessionId, targets, callback), true); + final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); + if (sessionInfo == null) return; + resolveService(sessionId, true, sessionInfo.mUsesPeopleService, + s -> s.sortAppTargets(sessionId, targets, callback)); } /** @@ -160,10 +174,12 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId, @NonNull IPredictionCallback callback) { - final boolean serviceExists = resolveService(sessionId, s -> - s.registerPredictionUpdates(sessionId, callback), false); final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); - if (serviceExists && sessionInfo != null) { + if (sessionInfo == null) return; + final boolean serviceExists = resolveService(sessionId, false, + sessionInfo.mUsesPeopleService, + s -> s.registerPredictionUpdates(sessionId, callback)); + if (serviceExists) { sessionInfo.addCallbackLocked(callback); } } @@ -174,10 +190,12 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId, @NonNull IPredictionCallback callback) { - final boolean serviceExists = resolveService(sessionId, s -> - s.unregisterPredictionUpdates(sessionId, callback), false); final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); - if (serviceExists && sessionInfo != null) { + if (sessionInfo == null) return; + final boolean serviceExists = resolveService(sessionId, false, + sessionInfo.mUsesPeopleService, + s -> s.unregisterPredictionUpdates(sessionId, callback)); + if (serviceExists) { sessionInfo.removeCallbackLocked(callback); } } @@ -187,7 +205,10 @@ public class AppPredictionPerUserService extends */ @GuardedBy("mLock") public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) { - resolveService(sessionId, s -> s.requestPredictionUpdate(sessionId), true); + final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); + if (sessionInfo == null) return; + resolveService(sessionId, true, sessionInfo.mUsesPeopleService, + s -> s.requestPredictionUpdate(sessionId)); } /** @@ -195,12 +216,14 @@ public class AppPredictionPerUserService extends */ @GuardedBy("mLock") public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) { - final boolean serviceExists = resolveService(sessionId, s -> - s.onDestroyPredictionSession(sessionId), false); - final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); - if (serviceExists && sessionInfo != null) { - sessionInfo.destroy(); + if (isDebug()) { + Slog.d(TAG, "onDestroyPredictionSessionLocked(): sessionId=" + sessionId); } + final AppPredictionSessionInfo sessionInfo = mSessionInfos.remove(sessionId); + if (sessionInfo == null) return; + resolveService(sessionId, false, sessionInfo.mUsesPeopleService, + s -> s.onDestroyPredictionSession(sessionId)); + sessionInfo.destroy(); } @Override @@ -291,27 +314,18 @@ public class AppPredictionPerUserService extends } for (AppPredictionSessionInfo sessionInfo : mSessionInfos.values()) { - sessionInfo.resurrectSessionLocked(this); - } - } - - private void removeAppPredictionSessionInfo(AppPredictionSessionId sessionId) { - if (isDebug()) { - Slog.d(TAG, "removeAppPredictionSessionInfo(): sessionId=" + sessionId); - } - synchronized (mLock) { - mSessionInfos.remove(sessionId); + sessionInfo.resurrectSessionLocked(this, sessionInfo.mToken); } } @GuardedBy("mLock") @Nullable - protected boolean resolveService(@NonNull final AppPredictionSessionId sessionId, - @NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb, - boolean sendImmediately) { - final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); - if (sessionInfo == null) return false; - if (sessionInfo.mUsesPeopleService) { + protected boolean resolveService( + @NonNull final AppPredictionSessionId sessionId, + boolean sendImmediately, + boolean usesPeopleService, + @NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb) { + if (usesPeopleService) { final IPredictionService service = LocalServices.getService(PeopleServiceInternal.class); if (service != null) { @@ -368,7 +382,9 @@ public class AppPredictionPerUserService extends private final AppPredictionContext mPredictionContext; private final boolean mUsesPeopleService; @NonNull - private final Consumer<AppPredictionSessionId> mRemoveSessionInfoAction; + final IBinder mToken; + @NonNull + final IBinder.DeathRecipient mDeathRecipient; private final RemoteCallbackList<IPredictionCallback> mCallbacks = new RemoteCallbackList<IPredictionCallback>() { @@ -388,14 +404,16 @@ public class AppPredictionPerUserService extends @NonNull final AppPredictionSessionId id, @NonNull final AppPredictionContext predictionContext, final boolean usesPeopleService, - @NonNull final Consumer<AppPredictionSessionId> removeSessionInfoAction) { + @NonNull final IBinder token, + @NonNull final IBinder.DeathRecipient deathRecipient) { if (DEBUG) { Slog.d(TAG, "Creating AppPredictionSessionInfo for session Id=" + id); } mSessionId = id; mPredictionContext = predictionContext; mUsesPeopleService = usesPeopleService; - mRemoveSessionInfoAction = removeSessionInfoAction; + mToken = token; + mDeathRecipient = deathRecipient; } void addCallbackLocked(IPredictionCallback callback) { @@ -414,23 +432,38 @@ public class AppPredictionPerUserService extends mCallbacks.unregister(callback); } + boolean linkToDeath() { + try { + mToken.linkToDeath(mDeathRecipient, 0); + } catch (RemoteException e) { + if (DEBUG) { + Slog.w(TAG, "Caller is dead before session can be started, sessionId: " + + mSessionId); + } + return false; + } + return true; + } + void destroy() { if (DEBUG) { Slog.d(TAG, "Removing all callbacks for session Id=" + mSessionId + " and " + mCallbacks.getRegisteredCallbackCount() + " callbacks."); } + if (mToken != null) { + mToken.unlinkToDeath(mDeathRecipient, 0); + } mCallbacks.kill(); - mRemoveSessionInfoAction.accept(mSessionId); } - void resurrectSessionLocked(AppPredictionPerUserService service) { + void resurrectSessionLocked(AppPredictionPerUserService service, IBinder token) { int callbackCount = mCallbacks.getRegisteredCallbackCount(); if (DEBUG) { Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked() + ") for session Id=" + mSessionId + " and " + callbackCount + " callbacks."); } - service.onCreatePredictionSessionLocked(mPredictionContext, mSessionId); + service.onCreatePredictionSessionLocked(mPredictionContext, mSessionId, token); mCallbacks.broadcast( callback -> service.registerPredictionUpdatesLocked(mSessionId, callback)); } diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 96d973ec5272..687af107590a 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -85,6 +85,7 @@ import java.util.Arrays; import java.util.Date; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; public class VibratorService extends IVibratorService.Stub implements InputManager.InputDeviceListener { @@ -114,6 +115,9 @@ public class VibratorService extends IVibratorService.Stub private static final VibrationAttributes DEFAULT_ATTRIBUTES = new VibrationAttributes.Builder().build(); + // Used to generate globally unique vibration ids. + private final AtomicInteger mNextVibrationId = new AtomicInteger(1); // 0 = no callback + // A mapping from the intensity adjustment to the scaling to apply, where the intensity // adjustment is defined as the delta between the default intensity level and the user selected // intensity level. It's important that we apply the scaling on the delta between the two so @@ -171,34 +175,34 @@ public class VibratorService extends IVibratorService.Stub private int mRingIntensity; private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>(); - static native long vibratorInit(); + static native long vibratorInit(OnCompleteListener listener); static native long vibratorGetFinalizer(); - static native boolean vibratorExists(long controllerPtr); + static native boolean vibratorExists(long nativeServicePtr); - static native void vibratorOn(long controllerPtr, long milliseconds, Vibration vibration); + static native void vibratorOn(long nativeServicePtr, long milliseconds, long vibrationId); - static native void vibratorOff(long controllerPtr); + static native void vibratorOff(long nativeServicePtr); - static native void vibratorSetAmplitude(long controllerPtr, int amplitude); + static native void vibratorSetAmplitude(long nativeServicePtr, int amplitude); - static native int[] vibratorGetSupportedEffects(long controllerPtr); + static native int[] vibratorGetSupportedEffects(long nativeServicePtr); - static native int[] vibratorGetSupportedPrimitives(long controllerPtr); + static native int[] vibratorGetSupportedPrimitives(long nativeServicePtr); static native long vibratorPerformEffect( - long controllerPtr, long effect, long strength, Vibration vibration); + long nativeServicePtr, long effect, long strength, long vibrationId); - static native void vibratorPerformComposedEffect(long controllerPtr, - VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration); + static native void vibratorPerformComposedEffect(long nativeServicePtr, + VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId); - static native void vibratorSetExternalControl(long controllerPtr, boolean enabled); + static native void vibratorSetExternalControl(long nativeServicePtr, boolean enabled); - static native long vibratorGetCapabilities(long controllerPtr); - static native void vibratorAlwaysOnEnable(long controllerPtr, long id, long effect, + static native long vibratorGetCapabilities(long nativeServicePtr); + static native void vibratorAlwaysOnEnable(long nativeServicePtr, long id, long effect, long strength); - static native void vibratorAlwaysOnDisable(long controllerPtr, long id); + static native void vibratorAlwaysOnDisable(long nativeServicePtr, long id); private final IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, @@ -220,12 +224,19 @@ public class VibratorService extends IVibratorService.Stub } }; + /** Listener for vibration completion callbacks from native. */ + public interface OnCompleteListener { + + /** Callback triggered when vibration is complete, identified by {@link Vibration#id}. */ + void onComplete(long vibrationId); + } + /** * Holder for a vibration to be played. This class can be shared with native methods for * hardware callback support. */ - @VisibleForTesting - public final class Vibration implements IBinder.DeathRecipient { + private final class Vibration implements IBinder.DeathRecipient { + public final IBinder token; // Start time in CLOCK_BOOTTIME base. public final long startTime; @@ -234,6 +245,7 @@ public class VibratorService extends IVibratorService.Stub // not to be affected by discontinuities created by RTC adjustments. public final long startTimeDebug; public final VibrationAttributes attrs; + public final long id; public final int uid; public final String opPkg; public final String reason; @@ -248,6 +260,7 @@ public class VibratorService extends IVibratorService.Stub VibrationAttributes attrs, int uid, String opPkg, String reason) { this.token = token; this.effect = effect; + this.id = mNextVibrationId.getAndIncrement(); this.startTime = SystemClock.elapsedRealtime(); this.startTimeDebug = System.currentTimeMillis(); this.attrs = attrs; @@ -268,19 +281,6 @@ public class VibratorService extends IVibratorService.Stub } } - /** Callback for when vibration is complete, to be called by native. */ - @VisibleForTesting - public void onComplete() { - synchronized (mLock) { - if (this == mCurrentVibration) { - if (DEBUG) { - Slog.d(TAG, "Vibration finished by callback, cleaning up"); - } - doCancelVibrateLocked(); - } - } - } - public boolean hasTimeoutLongerThan(long millis) { final long duration = effect.getDuration(); return duration >= 0 && duration > millis; @@ -385,14 +385,14 @@ public class VibratorService extends IVibratorService.Stub mNativeWrapper = injector.getNativeWrapper(); mH = injector.createHandler(Looper.myLooper()); - long controllerPtr = mNativeWrapper.vibratorInit(); + long nativeServicePtr = mNativeWrapper.vibratorInit(this::onVibrationComplete); long finalizerPtr = mNativeWrapper.vibratorGetFinalizer(); if (finalizerPtr != 0) { NativeAllocationRegistry registry = NativeAllocationRegistry.createMalloced( VibratorService.class.getClassLoader(), finalizerPtr); - registry.registerNativeAllocation(this, controllerPtr); + registry.registerNativeAllocation(this, nativeServicePtr); } // Reset the hardware to a default state, in case this is a runtime @@ -549,6 +549,19 @@ public class VibratorService extends IVibratorService.Stub } } + /** Callback for when vibration is complete, to be called by native. */ + @VisibleForTesting + public void onVibrationComplete(long vibrationId) { + synchronized (mLock) { + if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) { + if (DEBUG) { + Slog.d(TAG, "Vibration finished by callback, cleaning up"); + } + doCancelVibrateLocked(); + } + } + } + @Override // Binder call public boolean hasVibrator() { return doVibratorExists(); @@ -1266,18 +1279,18 @@ public class VibratorService extends IVibratorService.Stub return mNativeWrapper.vibratorExists(); } - /** Vibrates with native callback trigger for {@link Vibration#onComplete()}. */ + /** Vibrates with native callback trigger for {@link #onVibrationComplete(long)}. */ private void doVibratorOn(long millis, int amplitude, Vibration vib) { - doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib); + doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib.id); } /** Vibrates without native callback. */ private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) { - doVibratorOn(millis, amplitude, uid, attrs, /* vib= */ null); + doVibratorOn(millis, amplitude, uid, attrs, /* vibrationId= */ 0); } private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs, - @Nullable Vibration vib) { + long vibrationId) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn"); try { synchronized (mInputDeviceVibrators) { @@ -1299,7 +1312,7 @@ public class VibratorService extends IVibratorService.Stub // Note: ordering is important here! Many haptic drivers will reset their // amplitude when enabled, so we always have to enable first, then set the // amplitude. - mNativeWrapper.vibratorOn(millis, vib); + mNativeWrapper.vibratorOn(millis, vibrationId); doVibratorSetAmplitude(amplitude); } } @@ -1348,7 +1361,7 @@ public class VibratorService extends IVibratorService.Stub // Input devices don't support prebaked effect, so skip trying it with them. if (!usingInputDeviceVibrators) { long duration = mNativeWrapper.vibratorPerformEffect( - prebaked.getId(), prebaked.getEffectStrength(), vib); + prebaked.getId(), prebaked.getEffectStrength(), vib.id); if (duration > 0) { noteVibratorOnLocked(vib.uid, duration); return; @@ -1395,7 +1408,7 @@ public class VibratorService extends IVibratorService.Stub PrimitiveEffect[] primitiveEffects = composed.getPrimitiveEffects().toArray(new PrimitiveEffect[0]); - mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib); + mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib.id); // Composed effects don't actually give us an estimated duration, so we just guess here. noteVibratorOnLocked(vib.uid, 10 * primitiveEffects.length); @@ -1726,20 +1739,20 @@ public class VibratorService extends IVibratorService.Stub @VisibleForTesting public static class NativeWrapper { - private long mNativeControllerPtr = 0; + private long mNativeServicePtr = 0; /** Checks if vibrator exists on device. */ public boolean vibratorExists() { - return VibratorService.vibratorExists(mNativeControllerPtr); + return VibratorService.vibratorExists(mNativeServicePtr); } /** * Returns native pointer to newly created controller and initializes connection to vibrator * HAL service. */ - public long vibratorInit() { - mNativeControllerPtr = VibratorService.vibratorInit(); - return mNativeControllerPtr; + public long vibratorInit(OnCompleteListener listener) { + mNativeServicePtr = VibratorService.vibratorInit(listener); + return mNativeServicePtr; } /** Returns pointer to native finalizer function to be called by GC. */ @@ -1748,60 +1761,61 @@ public class VibratorService extends IVibratorService.Stub } /** Turns vibrator on for given time. */ - public void vibratorOn(long milliseconds, @Nullable Vibration vibration) { - VibratorService.vibratorOn(mNativeControllerPtr, milliseconds, vibration); + public void vibratorOn(long milliseconds, long vibrationId) { + VibratorService.vibratorOn(mNativeServicePtr, milliseconds, vibrationId); } /** Turns vibrator off. */ public void vibratorOff() { - VibratorService.vibratorOff(mNativeControllerPtr); + VibratorService.vibratorOff(mNativeServicePtr); } /** Sets the amplitude for the vibrator to run. */ public void vibratorSetAmplitude(int amplitude) { - VibratorService.vibratorSetAmplitude(mNativeControllerPtr, amplitude); + VibratorService.vibratorSetAmplitude(mNativeServicePtr, amplitude); } /** Returns all predefined effects supported by the device vibrator. */ public int[] vibratorGetSupportedEffects() { - return VibratorService.vibratorGetSupportedEffects(mNativeControllerPtr); + return VibratorService.vibratorGetSupportedEffects(mNativeServicePtr); } /** Returns all compose primitives supported by the device vibrator. */ public int[] vibratorGetSupportedPrimitives() { - return VibratorService.vibratorGetSupportedPrimitives(mNativeControllerPtr); + return VibratorService.vibratorGetSupportedPrimitives(mNativeServicePtr); } /** Turns vibrator on to perform one of the supported effects. */ - public long vibratorPerformEffect(long effect, long strength, Vibration vibration) { + public long vibratorPerformEffect(long effect, long strength, long vibrationId) { return VibratorService.vibratorPerformEffect( - mNativeControllerPtr, effect, strength, vibration); + mNativeServicePtr, effect, strength, vibrationId); } /** Turns vibrator on to perform one of the supported composed effects. */ public void vibratorPerformComposedEffect( - VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration) { - VibratorService.vibratorPerformComposedEffect(mNativeControllerPtr, effect, vibration); + VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) { + VibratorService.vibratorPerformComposedEffect(mNativeServicePtr, effect, + vibrationId); } /** Enabled the device vibrator to be controlled by another service. */ public void vibratorSetExternalControl(boolean enabled) { - VibratorService.vibratorSetExternalControl(mNativeControllerPtr, enabled); + VibratorService.vibratorSetExternalControl(mNativeServicePtr, enabled); } /** Returns all capabilities of the device vibrator. */ public long vibratorGetCapabilities() { - return VibratorService.vibratorGetCapabilities(mNativeControllerPtr); + return VibratorService.vibratorGetCapabilities(mNativeServicePtr); } /** Enable always-on vibration with given id and effect. */ public void vibratorAlwaysOnEnable(long id, long effect, long strength) { - VibratorService.vibratorAlwaysOnEnable(mNativeControllerPtr, id, effect, strength); + VibratorService.vibratorAlwaysOnEnable(mNativeServicePtr, id, effect, strength); } /** Disable always-on vibration for given id. */ public void vibratorAlwaysOnDisable(long id) { - VibratorService.vibratorAlwaysOnDisable(mNativeControllerPtr, id); + VibratorService.vibratorAlwaysOnDisable(mNativeServicePtr, id); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 6e1e3d0a9a9a..2d803437beb9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -83,6 +83,7 @@ import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS; import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER; import static android.text.format.DateUtils.DAY_IN_MILLIS; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK; @@ -117,7 +118,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA import static com.android.server.am.MemoryStatUtil.hasMemcg; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK; @@ -319,6 +319,7 @@ import com.android.internal.os.IResultReceiver; import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.TransferPipe; import com.android.internal.os.Zygote; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastPrintWriter; @@ -4946,9 +4947,8 @@ public class ActivityManagerService extends IActivityManager.Stub notifyPackageUse(instr.mClass.getPackageName(), PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION); } - if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc " - + processName + " with config " - + app.getWindowProcessController().getConfiguration()); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Binding proc %s with config %s", + processName, app.getWindowProcessController().getConfiguration()); ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info; app.compat = compatibilityInfoForPackage(appInfo); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 06ef58f8cc61..5447605a36d1 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -500,8 +500,8 @@ import java.util.concurrent.atomic.AtomicBoolean; sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType); } - /*package*/ void postSetModeOwnerPid(int pid) { - sendIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid); + /*package*/ void postSetModeOwnerPid(int pid, int mode) { + sendIIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid, mode); } /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) { @@ -977,7 +977,9 @@ import java.util.concurrent.atomic.AtomicBoolean; synchronized (mDeviceStateLock) { if (mModeOwnerPid != msg.arg1) { mModeOwnerPid = msg.arg1; - updateSpeakerphoneOn("setNewModeOwner"); + if (msg.arg2 != AudioSystem.MODE_RINGTONE) { + updateSpeakerphoneOn("setNewModeOwner"); + } if (mModeOwnerPid != 0) { mBtHelper.disconnectBluetoothSco(mModeOwnerPid); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 673ca1f3da86..3f29eb5636ea 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -3747,13 +3747,15 @@ public class AudioService extends IAudioService.Stub private final IBinder mCb; // To be notified of client's death private final int mPid; private final int mUid; - private String mPackage; + private final boolean mIsPrivileged; + private final String mPackage; private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client - SetModeDeathHandler(IBinder cb, int pid, int uid, String caller) { + SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, String caller) { mCb = cb; mPid = pid; mUid = uid; + mIsPrivileged = isPrivileged; mPackage = caller; } @@ -3765,12 +3767,13 @@ public class AudioService extends IAudioService.Stub if (index < 0) { Log.w(TAG, "unregistered setMode() client died"); } else { - newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, mUid, TAG); + newModeOwnerPid = setModeInt( + AudioSystem.MODE_NORMAL, mCb, mPid, mUid, mIsPrivileged, TAG); } } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid); + mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, AudioService.this.getMode()); } public int getPid() { @@ -3796,6 +3799,10 @@ public class AudioService extends IAudioService.Stub public String getPackage() { return mPackage; } + + public boolean isPrivileged() { + return mIsPrivileged; + } } /** @see AudioManager#setMode(int) */ @@ -3847,18 +3854,19 @@ public class AudioService extends IAudioService.Stub + " without permission or being mode owner"); return; } - newModeOwnerPid = setModeInt( - mode, cb, callingPid, Binder.getCallingUid(), callingPackage); + newModeOwnerPid = setModeInt(mode, cb, callingPid, Binder.getCallingUid(), + hasModifyPhoneStatePermission, callingPackage); } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid); + mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, getMode()); } // setModeInt() returns a valid PID if the audio mode was successfully set to // any mode other than NORMAL. @GuardedBy("mDeviceBroker.mSetModeLock") - private int setModeInt(int mode, IBinder cb, int pid, int uid, String caller) { + private int setModeInt( + int mode, IBinder cb, int pid, int uid, boolean isPrivileged, String caller) { if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", uid=" + uid + ", caller=" + caller + ")"); @@ -3910,7 +3918,7 @@ public class AudioService extends IAudioService.Stub } } else { if (hdlr == null) { - hdlr = new SetModeDeathHandler(cb, pid, uid, caller); + hdlr = new SetModeDeathHandler(cb, pid, uid, isPrivileged, caller); } // Register for client death notification try { @@ -3969,7 +3977,8 @@ public class AudioService extends IAudioService.Stub // change of mode may require volume to be re-applied on some devices updateAbsVolumeMultiModeDevices(oldMode, actualMode); - if (actualMode == AudioSystem.MODE_IN_COMMUNICATION) { + if (actualMode == AudioSystem.MODE_IN_COMMUNICATION + && !hdlr.isPrivileged()) { sendMsg(mAudioHandler, MSG_CHECK_MODE_FOR_UID, SENDMSG_QUEUE, @@ -6519,8 +6528,8 @@ public class AudioService extends IAudioService.Stub CHECK_MODE_FOR_UID_PERIOD_MS); break; } - // For now just log the fact that an app is hogging the audio mode. - // TODO(b/160260850): remove abusive app from audio mode stack. + setModeInt(AudioSystem.MODE_NORMAL, h.getBinder(), h.getPid(), h.getUid(), + h.isPrivileged(), "MSG_CHECK_MODE_FOR_UID"); mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid())); } break; diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index a75a80a606eb..4c63eb488118 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -174,7 +174,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms); } - List<UserInfo> users = mUserManager.getUsers(true); // exclude dying users + List<UserInfo> users = mUserManager.getAliveUsers(); if (users != null) { for (UserInfo user : users) { mUsers.add(user.id); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 5484bfca5851..7175489614ea 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -1463,7 +1463,7 @@ public class Vpn { final long token = Binder.clearCallingIdentity(); List<UserInfo> users; try { - users = UserManager.get(mContext).getUsers(true); + users = UserManager.get(mContext).getAliveUsers(); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java index 3f949bab8a2e..653323de8d60 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java @@ -25,7 +25,7 @@ import java.util.Arrays; * A helper class to build {@link HdmiCecMessage} from various cec commands. */ public class HdmiCecMessageBuilder { - private static final int OSD_NAME_MAX_LENGTH = 13; + private static final int OSD_NAME_MAX_LENGTH = 14; private HdmiCecMessageBuilder() {} diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 71f1833854ce..534533f2fc97 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -1133,9 +1133,7 @@ public class LocationManagerService extends ILocationManager.Stub { return; } - String dumpFilter = args.length == 0 ? null : args[0]; - - ipw.println("Location Manager State:"); + ipw.print("Location Manager State:"); ipw.increaseIndent(); ipw.println("Elapsed Realtime: " + TimeUtils.formatDuration(SystemClock.elapsedRealtime())); @@ -1166,34 +1164,28 @@ public class LocationManagerService extends ILocationManager.Stub { ipw.println( "Location Controller Extra Package: " + mExtraLocationControllerPackage + (mExtraLocationControllerPackageEnabled ? " [enabled]" - : "[disabled]")); + : " [disabled]")); } } ipw.println("Location Providers:"); ipw.increaseIndent(); for (LocationProviderManager manager : mProviderManagers) { - if (dumpFilter == null || manager.getName().equals(dumpFilter)) { - manager.dump(fd, ipw, args); - } + manager.dump(fd, ipw, args); } ipw.decreaseIndent(); - if (dumpFilter == null || GPS_PROVIDER.equals(dumpFilter)) { - if (mGnssManagerService != null) { - ipw.println("GNSS Manager:"); - ipw.increaseIndent(); - mGnssManagerService.dump(fd, ipw, args); - ipw.decreaseIndent(); - } - } - - if (dumpFilter == null || "geofence".equals(dumpFilter)) { - ipw.println("Geofence Manager:"); + if (mGnssManagerService != null) { + ipw.println("GNSS Manager:"); ipw.increaseIndent(); - mGeofenceManager.dump(fd, ipw, args); + mGnssManagerService.dump(fd, ipw, args); ipw.decreaseIndent(); } + + ipw.println("Geofence Manager:"); + ipw.increaseIndent(); + mGeofenceManager.dump(fd, ipw, args); + ipw.decreaseIndent(); } private class LocalService extends LocationManagerInternal { diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java index 05aa3150cfef..66245a279666 100644 --- a/services/core/java/com/android/server/location/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/LocationProviderManager.java @@ -84,7 +84,7 @@ import com.android.server.LocalServices; import com.android.server.PendingIntentUtils; import com.android.server.location.LocationPermissions.PermissionLevel; import com.android.server.location.listeners.ListenerMultiplexer; -import com.android.server.location.listeners.RemovableListenerRegistration; +import com.android.server.location.listeners.RemoteListenerRegistration; import com.android.server.location.util.AppForegroundHelper; import com.android.server.location.util.AppForegroundHelper.AppForegroundListener; import com.android.server.location.util.AppOpsHelper; @@ -154,15 +154,8 @@ class LocationProviderManager extends @Override public void deliverOnLocationChanged(Location location, - @Nullable Runnable onCompleteCallback) - throws RemoteException { - mListener.onLocationChanged(location, - onCompleteCallback == null ? null : new IRemoteCallback.Stub() { - @Override - public void sendResult(Bundle data) { - onCompleteCallback.run(); - } - }); + @Nullable Runnable onCompleteCallback) throws RemoteException { + mListener.onLocationChanged(location, SingleUseCallback.wrap(onCompleteCallback)); } @Override @@ -221,7 +214,7 @@ class LocationProviderManager extends } protected abstract class Registration extends - RemovableListenerRegistration<LocationRequest, LocationTransport> { + RemoteListenerRegistration<LocationRequest, LocationTransport> { @PermissionLevel protected final int mPermissionLevel; private final WorkSource mWorkSource; @@ -306,11 +299,20 @@ class LocationProviderManager extends } @Override - protected final void onInactive() { + protected final ListenerOperation<LocationTransport> onInactive() { onHighPowerUsageChanged(); if (!getRequest().getHideFromAppOps()) { mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey()); } + return null; + } + + @Override + public final <Listener> void onOperationFailure(ListenerOperation<Listener> operation, + Exception e) { + synchronized (mLock) { + super.onOperationFailure(operation, e); + } } @Override @@ -818,6 +820,12 @@ class LocationProviderManager extends @GuardedBy("mLock") @Override protected void onProviderListenerRegister() { + try { + ((IBinder) getKey()).linkToDeath(this, 0); + } catch (RemoteException e) { + remove(); + } + mExpirationRealtimeMs = getRequest().getExpirationRealtimeMs( SystemClock.elapsedRealtime()); @@ -829,12 +837,6 @@ class LocationProviderManager extends 0, this, FgThread.getHandler(), getWorkSource()); } - try { - ((IBinder) getKey()).linkToDeath(this, 0); - } catch (RemoteException e) { - remove(); - } - // start listening for provider enabled/disabled events addEnabledListener(this); @@ -1058,8 +1060,13 @@ class LocationProviderManager extends mUserInfoHelper.addListener(mUserChangedListener); mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener); - // initialize enabled state - onUserStarted(UserHandle.USER_ALL); + long identity = Binder.clearCallingIdentity(); + try { + // initialize enabled state + onUserStarted(UserHandle.USER_ALL); + } finally { + Binder.restoreCallingIdentity(identity); + } } } @@ -1069,10 +1076,15 @@ class LocationProviderManager extends mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener); // notify and remove all listeners - onUserStopped(UserHandle.USER_ALL); - removeRegistrationIf(key -> true); - mEnabledListeners.clear(); + long identity = Binder.clearCallingIdentity(); + try { + onUserStopped(UserHandle.USER_ALL); + removeRegistrationIf(key -> true); + } finally { + Binder.restoreCallingIdentity(identity); + } + mEnabledListeners.clear(); mStarted = false; } } @@ -1133,14 +1145,26 @@ class LocationProviderManager extends public void setRealProvider(AbstractLocationProvider provider) { synchronized (mLock) { Preconditions.checkState(mStarted); - mProvider.setRealProvider(provider); + + long identity = Binder.clearCallingIdentity(); + try { + mProvider.setRealProvider(provider); + } finally { + Binder.restoreCallingIdentity(identity); + } } } public void setMockProvider(@Nullable MockProvider provider) { synchronized (mLock) { Preconditions.checkState(mStarted); - mProvider.setMockProvider(provider); + + long identity = Binder.clearCallingIdentity(); + try { + mProvider.setMockProvider(provider); + } finally { + Binder.restoreCallingIdentity(identity); + } // when removing a mock provider, also clear any mock last locations and reset the // location fudger. the mock provider could have been used to infer the current @@ -1162,7 +1186,12 @@ class LocationProviderManager extends throw new IllegalArgumentException(mName + " provider is not a test provider"); } - mProvider.setMockProviderAllowed(enabled); + long identity = Binder.clearCallingIdentity(); + try { + mProvider.setMockProviderAllowed(enabled); + } finally { + Binder.restoreCallingIdentity(identity); + } } } @@ -1172,15 +1201,20 @@ class LocationProviderManager extends throw new IllegalArgumentException(mName + " provider is not a test provider"); } - String locationProvider = location.getProvider(); - if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) { - // The location has an explicit provider that is different from the mock - // provider name. The caller may be trying to fool us via b/33091107. - EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(), - mName + "!=" + locationProvider); - } + long identity = Binder.clearCallingIdentity(); + try { + String locationProvider = location.getProvider(); + if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) { + // The location has an explicit provider that is different from the mock + // provider name. The caller may be trying to fool us via b/33091107. + EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(), + mName + "!=" + locationProvider); + } - mProvider.setMockProviderLocation(location); + mProvider.setMockProviderLocation(location); + } finally { + Binder.restoreCallingIdentity(identity); + } } } @@ -1271,7 +1305,7 @@ class LocationProviderManager extends } } - public void getCurrentLocation(LocationRequest request, CallerIdentity identity, + public void getCurrentLocation(LocationRequest request, CallerIdentity callerIdentity, int permissionLevel, ICancellationSignal cancellationTransport, ILocationCallback callback) { Preconditions.checkArgument(mName.equals(request.getProvider())); @@ -1283,12 +1317,12 @@ class LocationProviderManager extends GetCurrentLocationListenerRegistration registration = new GetCurrentLocationListenerRegistration( request, - identity, + callerIdentity, new GetCurrentLocationTransport(callback), permissionLevel); synchronized (mLock) { - Location lastLocation = getLastLocation(request, identity, permissionLevel); + Location lastLocation = getLastLocation(request, callerIdentity, permissionLevel); if (lastLocation != null) { long locationAgeMs = NANOSECONDS.toMillis( SystemClock.elapsedRealtimeNanos() @@ -1306,7 +1340,13 @@ class LocationProviderManager extends } // if last location isn't good enough then we add a location request - addRegistration(callback.asBinder(), registration); + long identity = Binder.clearCallingIdentity(); + try { + addRegistration(callback.asBinder(), registration); + } finally { + Binder.restoreCallingIdentity(identity); + } + CancellationSignal cancellationSignal = CancellationSignal.fromTransport( cancellationTransport); if (cancellationSignal != null) { @@ -1321,48 +1361,73 @@ class LocationProviderManager extends } public void sendExtraCommand(int uid, int pid, String command, Bundle extras) { - mProvider.sendExtraCommand(uid, pid, command, extras); + long identity = Binder.clearCallingIdentity(); + try { + mProvider.sendExtraCommand(uid, pid, command, extras); + } finally { + Binder.restoreCallingIdentity(identity); + } } - public void registerLocationRequest(LocationRequest request, CallerIdentity identity, + public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity, @PermissionLevel int permissionLevel, ILocationListener listener) { Preconditions.checkArgument(mName.equals(request.getProvider())); synchronized (mLock) { - addRegistration( - listener.asBinder(), - new LocationListenerRegistration( - request, - identity, - new LocationListenerTransport(listener), - permissionLevel)); + long identity = Binder.clearCallingIdentity(); + try { + addRegistration( + listener.asBinder(), + new LocationListenerRegistration( + request, + callerIdentity, + new LocationListenerTransport(listener), + permissionLevel)); + } finally { + Binder.restoreCallingIdentity(identity); + } } } - public void registerLocationRequest(LocationRequest request, CallerIdentity identity, + public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity, @PermissionLevel int permissionLevel, PendingIntent pendingIntent) { Preconditions.checkArgument(mName.equals(request.getProvider())); synchronized (mLock) { - addRegistration( - pendingIntent, - new LocationPendingIntentRegistration( - request, - identity, - new LocationPendingIntentTransport(mContext, pendingIntent), - permissionLevel)); + long identity = Binder.clearCallingIdentity(); + try { + addRegistration( + pendingIntent, + new LocationPendingIntentRegistration( + request, + callerIdentity, + new LocationPendingIntentTransport(mContext, pendingIntent), + permissionLevel)); + } finally { + Binder.restoreCallingIdentity(identity); + } } } public void unregisterLocationRequest(ILocationListener listener) { synchronized (mLock) { - removeRegistration(listener.asBinder()); + long identity = Binder.clearCallingIdentity(); + try { + removeRegistration(listener.asBinder()); + } finally { + Binder.restoreCallingIdentity(identity); + } } } public void unregisterLocationRequest(PendingIntent pendingIntent) { synchronized (mLock) { - removeRegistration(pendingIntent); + long identity = Binder.clearCallingIdentity(); + try { + removeRegistration(pendingIntent); + } finally { + Binder.restoreCallingIdentity(identity); + } } } @@ -1950,4 +2015,50 @@ class LocationProviderManager extends } } } + + private static class SingleUseCallback extends IRemoteCallback.Stub { + + @Nullable + public static IRemoteCallback wrap(@Nullable Runnable callback) { + return callback == null ? null : new SingleUseCallback(callback); + } + + @GuardedBy("this") + @Nullable private Runnable mCallback; + + private SingleUseCallback(Runnable callback) { + mCallback = Objects.requireNonNull(callback); + } + + @Override + public void sendResult(Bundle data) { + Runnable callback; + synchronized (this) { + callback = mCallback; + mCallback = null; + } + + // prevent this callback from being run more than once - otherwise this could provide an + // attack vector for a malicious app to break assumptions on how many times a callback + // may be invoked, and thus crash system server. + if (callback == null) { + return; + } + + long identity = Binder.clearCallingIdentity(); + try { + callback.run(); + } catch (RuntimeException e) { + // since this is within a oneway binder transaction there is nowhere + // for exceptions to go - move onto another thread to crash system + // server so we find out about it + FgThread.getExecutor().execute(() -> { + throw new AssertionError(e); + }); + throw e; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } } diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java index 2d9734ef0553..2d7f02873b8f 100644 --- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java +++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java @@ -32,6 +32,7 @@ import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationRequest; import android.location.util.identity.CallerIdentity; +import android.os.Binder; import android.os.PowerManager; import android.os.SystemClock; import android.os.WorkSource; @@ -291,17 +292,28 @@ public class GeofenceManager extends @Nullable String attributionTag) { LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_FINE); - CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, - AppOpsManager.toReceiverId(pendingIntent)); - addRegistration(new GeofenceKey(pendingIntent, geofence), - new GeofenceRegistration(geofence, identity, pendingIntent)); + CallerIdentity callerIdentity = CallerIdentity.fromBinder(mContext, packageName, + attributionTag, AppOpsManager.toReceiverId(pendingIntent)); + + long identity = Binder.clearCallingIdentity(); + try { + addRegistration(new GeofenceKey(pendingIntent, geofence), + new GeofenceRegistration(geofence, callerIdentity, pendingIntent)); + } finally { + Binder.restoreCallingIdentity(identity); + } } /** * Removes the geofence associated with the PendingIntent. */ public void removeGeofence(PendingIntent pendingIntent) { - removeRegistrationIf(key -> key.getPendingIntent().equals(pendingIntent)); + long identity = Binder.clearCallingIdentity(); + try { + removeRegistrationIf(key -> key.getPendingIntent().equals(pendingIntent)); + } finally { + Binder.restoreCallingIdentity(identity); + } } @Override diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java index 1b599b026c38..a9fdacca9a06 100644 --- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java @@ -25,6 +25,7 @@ import android.annotation.Nullable; import android.location.LocationManagerInternal; import android.location.LocationManagerInternal.ProviderEnabledListener; import android.location.util.identity.CallerIdentity; +import android.os.Binder; import android.os.IBinder; import android.os.IInterface; import android.os.Process; @@ -218,16 +219,27 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter /** * Adds a listener with the given identity and request. */ - protected void addListener(TRequest request, CallerIdentity identity, TListener listener) { - addRegistration(listener.asBinder(), - new GnssListenerRegistration(request, identity, listener)); + protected void addListener(TRequest request, CallerIdentity callerIdentity, + TListener listener) { + long identity = Binder.clearCallingIdentity(); + try { + addRegistration(listener.asBinder(), + new GnssListenerRegistration(request, callerIdentity, listener)); + } finally { + Binder.restoreCallingIdentity(identity); + } } /** * Removes the given listener. */ public void removeListener(TListener listener) { - removeRegistration(listener.asBinder()); + long identity = Binder.clearCallingIdentity(); + try { + removeRegistration(listener.asBinder()); + } finally { + Binder.restoreCallingIdentity(identity); + } } @Override diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index 850cf7f4b7ce..5c30fe840073 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -2022,6 +2022,21 @@ public class GnssLocationProvider extends AbstractLocationProvider implements @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + boolean dumpAll = false; + + int opti = 0; + while (opti < args.length) { + String opt = args[opti]; + if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') { + break; + } + opti++; + if ("-a".equals(opt)) { + dumpAll = true; + break; + } + } + StringBuilder s = new StringBuilder(); s.append("mStarted=").append(mStarted).append(" (changed "); TimeUtils.formatDuration(SystemClock.elapsedRealtime() @@ -2053,9 +2068,11 @@ public class GnssLocationProvider extends AbstractLocationProvider implements s.append("]\n"); } s.append(mGnssMetrics.dumpGnssMetricsAsText()); - s.append("native internal state: \n"); - s.append(" ").append(native_get_internal_state()); - s.append("\n"); + if (dumpAll) { + s.append("native internal state: \n"); + s.append(" ").append(native_get_internal_state()); + s.append("\n"); + } pw.append(s); } diff --git a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java index bd8bce8f6d52..58aabdad056f 100644 --- a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java @@ -32,10 +32,10 @@ import android.util.Log; * @param <TListener> listener type */ public abstract class BinderListenerRegistration<TRequest, TListener> extends - RemovableListenerRegistration<TRequest, TListener> implements Binder.DeathRecipient { + RemoteListenerRegistration<TRequest, TListener> implements Binder.DeathRecipient { /** - * Interface to allowed binder retrieval when keys are not themselves IBinder. + * Interface to allow binder retrieval when keys are not themselves IBinders. */ public interface BinderKey { /** diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java index f94de9be0cfe..8a6b8aa1e463 100644 --- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java @@ -18,12 +18,9 @@ package com.android.server.location.listeners; import android.annotation.NonNull; import android.annotation.Nullable; -import android.os.Binder; import android.os.Build; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.IndentingPrintWriter; -import android.util.Pair; import com.android.internal.annotations.GuardedBy; import com.android.internal.listeners.ListenerExecutor.ListenerOperation; @@ -31,8 +28,10 @@ import com.android.internal.util.Preconditions; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; +import java.util.Map.Entry; import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; @@ -42,7 +41,7 @@ import java.util.function.Predicate; * divided into two categories, active registrations and inactive registrations, as defined by * {@link #isActive(ListenerRegistration)}. If a registration's active state changes, * {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration - * whose active state may have changed. + * whose active state may have changed. Listeners will only be invoked for active registrations. * * Callbacks invoked for various changes will always be ordered according to this lifecycle list: * @@ -64,14 +63,6 @@ import java.util.function.Predicate; * {@link #removeRegistration(Object, ListenerRegistration)}, not via any other removal method. This * ensures re-entrant removal does not accidentally remove the incorrect registration. * - * All callbacks will be invoked with a cleared binder identity. - * - * Listeners owned by other processes will be run on a direct executor (and thus while holding a - * lock). Listeners owned by the same process this multiplexer is in will be run asynchronously (and - * thus without holding a lock). The underlying assumption is that listeners owned by other - * processes will simply be forwarding the call to those other processes and perhaps performing - * simple bookkeeping, with no potential for deadlock. - * * @param <TKey> key type * @param <TRequest> request type * @param <TListener> listener type @@ -149,46 +140,51 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, } /** - * Invoked before the first registration occurs. This is a convenient entry point for - * registering listeners, etc, which only need to be present while there are any registrations. + * Invoked when the multiplexer goes from having no registrations to having some registrations. + * This is a convenient entry point for registering listeners, etc, which only need to be + * present while there are any registrations. Invoked while holding the multiplexer's internal + * lock. */ protected void onRegister() {} /** - * Invoked after the last unregistration occurs. This is a convenient entry point for - * unregistering listeners, etc, which only need to be present while there are any - * registrations. + * Invoked when the multiplexer goes from having some registrations to having no registrations. + * This is a convenient entry point for unregistering listeners, etc, which only need to be + * present while there are any registrations. Invoked while holding the multiplexer's internal + * lock. */ protected void onUnregister() {} /** - * Invoked when a registration is added. + * Invoked when a registration is added. Invoked while holding the multiplexer's internal lock. */ protected void onRegistrationAdded(@NonNull TKey key, @NonNull TRegistration registration) {} /** - * Invoked when a registration is removed. + * Invoked when a registration is removed. Invoked while holding the multiplexer's internal + * lock. */ protected void onRegistrationRemoved(@NonNull TKey key, @NonNull TRegistration registration) {} /** - * Invoked when the manager goes from having no active registrations to having some active + * Invoked when the multiplexer goes from having no active registrations to having some active * registrations. This is a convenient entry point for registering listeners, etc, which only - * need to be present while there are active registrations. + * need to be present while there are active registrations. Invoked while holding the + * multiplexer's internal lock. */ protected void onActive() {} /** - * Invoked when the manager goes from having some active registrations to having no active + * Invoked when the multiplexer goes from having some active registrations to having no active * registrations. This is a convenient entry point for unregistering listeners, etc, which only - * need to be present while there are active registrations. + * need to be present while there are active registrations. Invoked while holding the + * multiplexer's internal lock. */ protected void onInactive() {} /** - * Adds a new registration with the given key. Registration may fail if - * {@link ListenerRegistration#onRegister(Object)} returns false, in which case the registration - * will not be added. This method cannot be called to add a registration re-entrantly. + * Adds a new registration with the given key. This method cannot be called to add a + * registration re-entrantly. */ protected final void addRegistration(@NonNull TKey key, @NonNull TRegistration registration) { Objects.requireNonNull(key); @@ -204,7 +200,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, // involve removing a prior registration. note that try-with-resources ordering is // meaningful here as well. we want to close the reentrancy guard first, as this may // generate additional service updates, then close the update service buffer. - long identity = Binder.clearCallingIdentity(); try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire(); ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) { @@ -224,16 +219,13 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, registration.onRegister(key); onRegistrationAdded(key, registration); onRegistrationActiveChanged(registration); - } finally { - Binder.restoreCallingIdentity(identity); } } } /** - * Removes the registration with the given key. If unregistration occurs, - * {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This method - * cannot be called to remove a registration re-entrantly. + * Removes the registration with the given key. This method cannot be called to remove a + * registration re-entrantly. */ protected final void removeRegistration(@NonNull Object key) { synchronized (mRegistrations) { @@ -250,9 +242,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, } /** - * Removes all registrations with keys that satisfy the given predicate. If unregistration - * occurs, {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This - * method cannot be called to remove a registration re-entrantly. + * Removes all registrations with keys that satisfy the given predicate. This method cannot be + * called to remove a registration re-entrantly. */ protected final void removeRegistrationIf(@NonNull Predicate<TKey> predicate) { synchronized (mRegistrations) { @@ -281,11 +272,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, /** * Removes the given registration with the given key. If the given key has a different - * registration at the time this method is called, nothing happens. If unregistration occurs, - * {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This method - * allows for re-entrancy, and may be called to remove a registration re-entrantly. In this case - * the registration will immediately be marked inactive and unregistered, and will be removed - * completely at some later time. + * registration at the time this method is called, nothing happens. This method allows for + * re-entrancy, and may be called to remove a registration re-entrantly. */ protected final void removeRegistration(@NonNull Object key, @NonNull ListenerRegistration<?, ?> registration) { @@ -324,7 +312,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, // in multiple service updates. note that try-with-resources ordering is meaningful here as // well. we want to close the reentrancy guard first, as this may generate additional // service updates, then close the update service buffer. - long identity = Binder.clearCallingIdentity(); try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire(); ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) { @@ -337,8 +324,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, onUnregister(); } } - } finally { - Binder.restoreCallingIdentity(identity); } } @@ -362,38 +347,46 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, } } - long identity = Binder.clearCallingIdentity(); - try { - if (actives.isEmpty()) { + if (actives.isEmpty()) { + mCurrentRequest = null; + if (mServiceRegistered) { + mServiceRegistered = false; mCurrentRequest = null; - if (mServiceRegistered) { - mServiceRegistered = false; - mCurrentRequest = null; - unregisterWithService(); - } - return; + unregisterWithService(); } + return; + } - TMergedRequest merged = mergeRequests(actives); - if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) { - if (mServiceRegistered) { - mServiceRegistered = reregisterWithService(mCurrentRequest, merged); - } else { - mServiceRegistered = registerWithService(merged); - } - if (mServiceRegistered) { - mCurrentRequest = merged; - } else { - mCurrentRequest = null; - } + TMergedRequest merged = mergeRequests(actives); + if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) { + if (mServiceRegistered) { + mServiceRegistered = reregisterWithService(mCurrentRequest, merged); + } else { + mServiceRegistered = registerWithService(merged); + } + if (mServiceRegistered) { + mCurrentRequest = merged; + } else { + mCurrentRequest = null; } - } finally { - Binder.restoreCallingIdentity(identity); } } } /** + * Clears currently stored service state, and invokes {@link #updateService()} to force a new + * call to {@link #registerWithService(Object)} if necessary. This is useful, for instance, if + * the backing service has crashed or otherwise lost state, and needs to be re-initialized. + */ + protected final void resetService() { + synchronized (mRegistrations) { + mServiceRegistered = false; + mCurrentRequest = null; + updateService(); + } + } + + /** * Begins buffering calls to {@link #updateService()} until {@link UpdateServiceLock#close()} * is called. This is useful to prevent extra work when combining multiple calls (for example, * buffering {@code updateService()} until after multiple adds/removes/updates occur. @@ -404,9 +397,9 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, /** * Evaluates the predicate on all registrations. The predicate should return true if the active - * state of the registration may have changed as a result. Any {@link #updateService()} - * invocations made while this method is executing will be deferred until after the method is - * complete so as to avoid redundant work. + * state of the registration may have changed as a result. If the active state of any + * registration has changed, {@link #updateService()} will automatically be invoked to handle + * the resulting changes. */ protected final void updateRegistrations(@NonNull Predicate<TRegistration> predicate) { synchronized (mRegistrations) { @@ -415,7 +408,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, // callbacks. note that try-with-resources ordering is meaningful here as well. we want // to close the reentrancy guard first, as this may generate additional service updates, // then close the update service buffer. - long identity = Binder.clearCallingIdentity(); try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire(); ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) { @@ -426,8 +418,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, onRegistrationActiveChanged(registration); } } - } finally { - Binder.restoreCallingIdentity(identity); } } } @@ -450,7 +440,10 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, execute(registration, operation); } } else { - registration.onInactive(); + ListenerOperation<TListener> operation = registration.onInactive(); + if (operation != null) { + execute(registration, operation); + } if (--mActiveRegistrationsCount == 0) { onInactive(); } @@ -468,7 +461,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, protected final void deliverToListeners( @NonNull Function<TRegistration, ListenerOperation<TListener>> function) { synchronized (mRegistrations) { - long identity = Binder.clearCallingIdentity(); try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) { final int size = mRegistrations.size(); for (int i = 0; i < size; i++) { @@ -480,8 +472,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, } } } - } finally { - Binder.restoreCallingIdentity(identity); } } } @@ -495,7 +485,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, */ protected final void deliverToListeners(@NonNull ListenerOperation<TListener> operation) { synchronized (mRegistrations) { - long identity = Binder.clearCallingIdentity(); try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) { final int size = mRegistrations.size(); for (int i = 0; i < size; i++) { @@ -504,8 +493,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, execute(registration, operation); } } - } finally { - Binder.restoreCallingIdentity(identity); } } } @@ -522,27 +509,26 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, /** * Dumps debug information. */ - public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mRegistrations) { - ipw.print("service: "); - dumpServiceState(ipw); - ipw.println(); + pw.print("service: "); + dumpServiceState(pw); + pw.println(); if (!mRegistrations.isEmpty()) { - ipw.println("listeners:"); + pw.println("listeners:"); - ipw.increaseIndent(); final int size = mRegistrations.size(); for (int i = 0; i < size; i++) { TRegistration registration = mRegistrations.valueAt(i); - ipw.print(registration); + pw.print(" "); + pw.print(registration); if (!registration.isActive()) { - ipw.println(" (inactive)"); + pw.println(" (inactive)"); } else { - ipw.println(); + pw.println(); } } - ipw.decreaseIndent(); } } } @@ -577,7 +563,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, @GuardedBy("mRegistrations") private int mGuardCount; @GuardedBy("mRegistrations") - private @Nullable ArraySet<Pair<Object, ListenerRegistration<?, ?>>> mScheduledRemovals; + private @Nullable ArraySet<Entry<Object, ListenerRegistration<?, ?>>> mScheduledRemovals; ReentrancyGuard() { mGuardCount = 0; @@ -602,7 +588,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, if (mScheduledRemovals == null) { mScheduledRemovals = new ArraySet<>(mRegistrations.size()); } - mScheduledRemovals.add(new Pair<>(key, registration)); + mScheduledRemovals.add(new AbstractMap.SimpleImmutableEntry<>(key, registration)); } ReentrancyGuard acquire() { @@ -612,7 +598,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, @Override public void close() { - ArraySet<Pair<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null; + ArraySet<Entry<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null; Preconditions.checkState(mGuardCount > 0); if (--mGuardCount == 0) { @@ -620,14 +606,15 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, mScheduledRemovals = null; } - if (scheduledRemovals != null) { - try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) { - final int size = scheduledRemovals.size(); - for (int i = 0; i < size; i++) { - Pair<Object, ListenerRegistration<?, ?>> pair = scheduledRemovals.valueAt( - i); - removeRegistration(pair.first, pair.second); - } + if (scheduledRemovals == null) { + return; + } + + try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) { + final int size = scheduledRemovals.size(); + for (int i = 0; i < size; i++) { + Entry<Object, ListenerRegistration<?, ?>> entry = scheduledRemovals.valueAt(i); + removeRegistration(entry.getKey(), entry.getValue()); } } } diff --git a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java index ac56c51568be..deb9660a1c82 100644 --- a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java @@ -17,55 +17,34 @@ package com.android.server.location.listeners; -import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; - import android.annotation.NonNull; import android.annotation.Nullable; -import android.location.util.identity.CallerIdentity; -import android.os.Process; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.listeners.ListenerExecutor; -import com.android.server.FgThread; import java.util.Objects; import java.util.concurrent.Executor; /** * A listener registration object which holds data associated with the listener, such as an optional - * request, and the identity of the listener owner. + * request, and an executor responsible for listener invocations. * * @param <TRequest> request type * @param <TListener> listener type */ public class ListenerRegistration<TRequest, TListener> implements ListenerExecutor { - @VisibleForTesting - public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor(); - private final Executor mExecutor; private final @Nullable TRequest mRequest; - private final CallerIdentity mIdentity; private boolean mActive; private volatile @Nullable TListener mListener; - protected ListenerRegistration(@Nullable TRequest request, CallerIdentity identity, + protected ListenerRegistration(Executor executor, @Nullable TRequest request, TListener listener) { - // if a client is in the same process as us, binder calls will execute synchronously and - // we shouldn't run callbacks directly since they might be run under lock and deadlock - if (identity.getPid() == Process.myPid()) { - // there's a slight loophole here for pending intents - pending intent callbacks can - // always be run on the direct executor since they're always asynchronous, but honestly - // you shouldn't be using pending intent callbacks within the same process anyways - mExecutor = IN_PROCESS_EXECUTOR; - } else { - mExecutor = DIRECT_EXECUTOR; - } - + mExecutor = Objects.requireNonNull(executor); mRequest = request; - mIdentity = Objects.requireNonNull(identity); mActive = false; mListener = Objects.requireNonNull(listener); } @@ -82,34 +61,34 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut } /** - * Returns the listener identity. - */ - public final CallerIdentity getIdentity() { - return mIdentity; - } - - /** - * May be overridden by subclasses. Invoked when registration occurs. + * May be overridden by subclasses. Invoked when registration occurs. Invoked while holding the + * owning multiplexer's internal lock. */ protected void onRegister(Object key) {} /** - * May be overridden by subclasses. Invoked when unregistration occurs. + * May be overridden by subclasses. Invoked when unregistration occurs. Invoked while holding + * the owning multiplexer's internal lock. */ protected void onUnregister() {} /** * May be overridden by subclasses. Invoked when this registration becomes active. If this - * returns a non-null operation, that operation will be invoked for the listener. + * returns a non-null operation, that operation will be invoked for the listener. Invoked + * while holding the owning multiplexer's internal lock. */ protected @Nullable ListenerOperation<TListener> onActive() { return null; } /** - * May be overridden by subclasses. Invoked when registration becomes inactive. + * May be overridden by subclasses. Invoked when registration becomes inactive. If this returns + * a non-null operation, that operation will be invoked for the listener. Invoked while holding + * the owning multiplexer's internal lock. */ - protected void onInactive() {} + protected @Nullable ListenerOperation<TListener> onInactive() { + return null; + } public final boolean isActive() { return mActive; @@ -136,8 +115,7 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut /** * May be overridden by subclasses, however should rarely be needed. Invoked when the listener * associated with this registration is unregistered, which may occur before the registration - * itself is unregistered. This immediately prevents the listener from being further invoked - * even if the various bookkeeping associated with unregistration has not occurred yet. + * itself is unregistered. This immediately prevents the listener from being further invoked. */ protected void onListenerUnregister() {}; diff --git a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java index b5d2ef6a72ec..7b6154eb0d00 100644 --- a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java @@ -30,7 +30,7 @@ import android.util.Log; * @param <TListener> listener type */ public abstract class PendingIntentListenerRegistration<TRequest, TListener> extends - RemovableListenerRegistration<TRequest, TListener> implements PendingIntent.CancelListener { + RemoteListenerRegistration<TRequest, TListener> implements PendingIntent.CancelListener { /** * Interface to allowed pending intent retrieval when keys are not themselves PendingIntents. diff --git a/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java new file mode 100644 index 000000000000..e4b0b190d34c --- /dev/null +++ b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java @@ -0,0 +1,75 @@ +/* + * 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.location.listeners; + + +import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; + +import android.annotation.Nullable; +import android.location.util.identity.CallerIdentity; +import android.os.Process; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.FgThread; + +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * A listener registration representing a remote (possibly from a different process) listener. + * Listeners from a different process will be run on a direct executor, since the x-process listener + * invocation should already be asynchronous. Listeners from the same process will be run on a + * normal executor, since in-process listener invocation may be synchronous. + * + * @param <TRequest> request type + * @param <TListener> listener type + */ +public abstract class RemoteListenerRegistration<TRequest, TListener> extends + RemovableListenerRegistration<TRequest, TListener> { + + @VisibleForTesting + public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor(); + + private static Executor chooseExecutor(CallerIdentity identity) { + // if a client is in the same process as us, binder calls will execute synchronously and + // we shouldn't run callbacks directly since they might be run under lock and deadlock + if (identity.getPid() == Process.myPid()) { + // there's a slight loophole here for pending intents - pending intent callbacks can + // always be run on the direct executor since they're always asynchronous, but honestly + // you shouldn't be using pending intent callbacks within the same process anyways + return IN_PROCESS_EXECUTOR; + } else { + return DIRECT_EXECUTOR; + } + } + + private final CallerIdentity mIdentity; + + protected RemoteListenerRegistration(String tag, @Nullable TRequest request, + CallerIdentity identity, TListener listener) { + super(tag, chooseExecutor(identity), request, listener); + mIdentity = Objects.requireNonNull(identity); + } + + /** + * Returns the listener identity. + */ + public final CallerIdentity getIdentity() { + return mIdentity; + } +} + diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java index 0698cca903f0..2383bece4e0a 100644 --- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java @@ -17,10 +17,10 @@ package com.android.server.location.listeners; import android.annotation.Nullable; -import android.location.util.identity.CallerIdentity; import android.util.Log; import java.util.Objects; +import java.util.concurrent.Executor; /** * A listener registration that stores its own key, and thus can remove itself. By default it will @@ -36,9 +36,9 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends private volatile @Nullable Object mKey; - protected RemovableListenerRegistration(String tag, @Nullable TRequest request, - CallerIdentity callerIdentity, TListener listener) { - super(request, callerIdentity, listener); + protected RemovableListenerRegistration(String tag, Executor executor, + @Nullable TRequest request, TListener listener) { + super(executor, request, listener); mTag = Objects.requireNonNull(tag); } @@ -70,7 +70,7 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends @Override public <Listener> void onOperationFailure(ListenerOperation<Listener> operation, Exception e) { - Log.w(mTag, "registration " + getIdentity() + " removed due to unexpected exception", e); + Log.w(mTag, "registration " + this + " removed due to unexpected exception", e); remove(); } diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java new file mode 100644 index 000000000000..d8745abb4a07 --- /dev/null +++ b/services/core/java/com/android/server/pm/ApkChecksums.java @@ -0,0 +1,310 @@ +/* + * 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.pm; + +import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA256; +import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA512; +import static android.content.pm.PackageManager.WHOLE_MD5; +import static android.content.pm.PackageManager.WHOLE_MERKLE_ROOT_4K_SHA256; +import static android.content.pm.PackageManager.WHOLE_SHA1; +import static android.content.pm.PackageManager.WHOLE_SHA256; +import static android.content.pm.PackageManager.WHOLE_SHA512; +import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256; +import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512; +import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; + +import android.annotation.Nullable; +import android.content.pm.FileChecksum; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.util.ArrayMap; +import android.util.Slog; +import android.util.apk.ApkSignatureSchemeV2Verifier; +import android.util.apk.ApkSignatureSchemeV3Verifier; +import android.util.apk.ApkSignatureSchemeV4Verifier; +import android.util.apk.ApkSignatureVerifier; +import android.util.apk.ApkSigningBlockUtils; +import android.util.apk.ByteBufferFactory; +import android.util.apk.SignatureInfo; +import android.util.apk.SignatureNotFoundException; +import android.util.apk.VerityBuilder; + +import com.android.server.security.VerityUtils; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.security.DigestException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Provides checksums for APK. + */ +public class ApkChecksums { + static final String TAG = "ApkChecksums"; + + // MessageDigest algorithms. + static final String ALGO_MD5 = "MD5"; + static final String ALGO_SHA1 = "SHA1"; + static final String ALGO_SHA256 = "SHA256"; + static final String ALGO_SHA512 = "SHA512"; + + /** + * Fetch or calculate checksums for the specific file. + * + * @param split split name, null for base + * @param file to fetch checksums for + * @param optional mask to fetch readily available checksums + * @param required mask to forcefully calculate if not available + * @param trustedInstallers array of certificate to trust, two specific cases: + * null - trust anybody, + * [] - trust nobody. + */ + public static List<FileChecksum> getFileChecksums(String split, File file, + @PackageManager.FileChecksumKind int optional, + @PackageManager.FileChecksumKind int required, + @Nullable Certificate[] trustedInstallers) { + final String filePath = file.getAbsolutePath(); + Map<Integer, FileChecksum> checksums = new ArrayMap<>(); + final int kinds = (optional | required); + // System enforced: FSI or v2/v3/v4 signatures. + if ((kinds & WHOLE_MERKLE_ROOT_4K_SHA256) != 0) { + // Hashes in fs-verity and IncFS are always verified. + FileChecksum checksum = extractHashFromFS(split, filePath); + if (checksum != null) { + checksums.put(checksum.getKind(), checksum); + } + } + if ((kinds & (PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512)) != 0) { + Map<Integer, FileChecksum> v2v3checksums = extractHashFromV2V3Signature( + split, filePath, kinds); + if (v2v3checksums != null) { + checksums.putAll(v2v3checksums); + } + } + + // TODO(b/160605420): Installer provided. + // TODO(b/160605420): Wait for Incremental to be fully loaded. + + // Manually calculating required checksums if not readily available. + if ((required & WHOLE_MERKLE_ROOT_4K_SHA256) != 0 && !checksums.containsKey( + WHOLE_MERKLE_ROOT_4K_SHA256)) { + try { + byte[] generatedRootHash = VerityBuilder.generateFsVerityRootHash( + filePath, /*salt=*/null, + new ByteBufferFactory() { + @Override + public ByteBuffer create(int capacity) { + return ByteBuffer.allocate(capacity); + } + }); + checksums.put(WHOLE_MERKLE_ROOT_4K_SHA256, + new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, generatedRootHash)); + } catch (IOException | NoSuchAlgorithmException | DigestException e) { + Slog.e(TAG, "Error calculating WHOLE_MERKLE_ROOT_4K_SHA256", e); + } + } + + calculateChecksumIfRequested(checksums, split, file, required, WHOLE_MD5); + calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA1); + calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA256); + calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA512); + + calculatePartialChecksumsIfRequested(checksums, split, file, required); + + return new ArrayList<>(checksums.values()); + } + + private static FileChecksum extractHashFromFS(String split, String filePath) { + // verity first + { + byte[] hash = VerityUtils.getFsverityRootHash(filePath); + if (hash != null) { + return new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash); + } + } + // v4 next + try { + ApkSignatureSchemeV4Verifier.VerifiedSigner signer = + ApkSignatureSchemeV4Verifier.extractCertificates(filePath); + byte[] hash = signer.contentDigests.getOrDefault(CONTENT_DIGEST_VERITY_CHUNKED_SHA256, + null); + if (hash != null) { + return new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash); + } + } catch (SignatureNotFoundException e) { + // Nothing + } catch (SecurityException e) { + Slog.e(TAG, "V4 signature error", e); + } + return null; + } + + private static Map<Integer, FileChecksum> extractHashFromV2V3Signature( + String split, String filePath, int kinds) { + Map<Integer, byte[]> contentDigests = null; + try { + contentDigests = ApkSignatureVerifier.verifySignaturesInternal(filePath, + PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2, + false).contentDigests; + } catch (PackageParser.PackageParserException e) { + Slog.e(TAG, "Signature verification error", e); + } + + if (contentDigests == null) { + return null; + } + + Map<Integer, FileChecksum> checksums = new ArrayMap<>(); + if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0) { + byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA256, null); + if (hash != null) { + checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA256, + new FileChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA256, hash)); + } + } + if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0) { + byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA512, null); + if (hash != null) { + checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA512, + new FileChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA512, hash)); + } + } + return checksums; + } + + private static String getMessageDigestAlgoForChecksumKind(int kind) + throws NoSuchAlgorithmException { + switch (kind) { + case WHOLE_MD5: + return ALGO_MD5; + case WHOLE_SHA1: + return ALGO_SHA1; + case WHOLE_SHA256: + return ALGO_SHA256; + case WHOLE_SHA512: + return ALGO_SHA512; + default: + throw new NoSuchAlgorithmException("Invalid checksum kind: " + kind); + } + } + + private static void calculateChecksumIfRequested(Map<Integer, FileChecksum> checksums, + String split, File file, int required, int kind) { + if ((required & kind) != 0 && !checksums.containsKey(kind)) { + final byte[] checksum = getFileChecksum(file, kind); + if (checksum != null) { + checksums.put(kind, new FileChecksum(split, kind, checksum)); + } + } + } + + private static byte[] getFileChecksum(File file, int kind) { + try (FileInputStream fis = new FileInputStream(file); + BufferedInputStream bis = new BufferedInputStream(fis)) { + byte[] dataBytes = new byte[512 * 1024]; + int nread = 0; + + final String algo = getMessageDigestAlgoForChecksumKind(kind); + MessageDigest md = MessageDigest.getInstance(algo); + while ((nread = bis.read(dataBytes)) != -1) { + md.update(dataBytes, 0, nread); + } + + return md.digest(); + } catch (IOException e) { + Slog.e(TAG, "Error reading " + file.getAbsolutePath() + " to compute hash.", e); + return null; + } catch (NoSuchAlgorithmException e) { + Slog.e(TAG, "Device does not support MessageDigest algorithm", e); + return null; + } + } + + private static int[] getContentDigestAlgos(boolean needSignatureSha256, + boolean needSignatureSha512) { + if (needSignatureSha256 && needSignatureSha512) { + // Signature block present, but no digests??? + return new int[]{CONTENT_DIGEST_CHUNKED_SHA256, CONTENT_DIGEST_CHUNKED_SHA512}; + } else if (needSignatureSha256) { + return new int[]{CONTENT_DIGEST_CHUNKED_SHA256}; + } else { + return new int[]{CONTENT_DIGEST_CHUNKED_SHA512}; + } + } + + private static int getChecksumKindForContentDigestAlgo(int contentDigestAlgo) { + switch (contentDigestAlgo) { + case CONTENT_DIGEST_CHUNKED_SHA256: + return PARTIAL_MERKLE_ROOT_1M_SHA256; + case CONTENT_DIGEST_CHUNKED_SHA512: + return PARTIAL_MERKLE_ROOT_1M_SHA512; + default: + return -1; + } + } + + private static void calculatePartialChecksumsIfRequested(Map<Integer, FileChecksum> checksums, + String split, File file, int required) { + boolean needSignatureSha256 = + (required & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0 && !checksums.containsKey( + PARTIAL_MERKLE_ROOT_1M_SHA256); + boolean needSignatureSha512 = + (required & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0 && !checksums.containsKey( + PARTIAL_MERKLE_ROOT_1M_SHA512); + if (!needSignatureSha256 && !needSignatureSha512) { + return; + } + + try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { + SignatureInfo signatureInfo = null; + try { + signatureInfo = ApkSignatureSchemeV3Verifier.findSignature(raf); + } catch (SignatureNotFoundException e) { + try { + signatureInfo = ApkSignatureSchemeV2Verifier.findSignature(raf); + } catch (SignatureNotFoundException ee) { + } + } + if (signatureInfo == null) { + Slog.e(TAG, "V2/V3 signatures not found in " + file.getAbsolutePath()); + return; + } + + final int[] digestAlgos = getContentDigestAlgos(needSignatureSha256, + needSignatureSha512); + byte[][] digests = ApkSigningBlockUtils.computeContentDigestsPer1MbChunk(digestAlgos, + raf.getFD(), signatureInfo); + for (int i = 0, size = digestAlgos.length; i < size; ++i) { + int checksumKind = getChecksumKindForContentDigestAlgo(digestAlgos[i]); + if (checksumKind != -1) { + checksums.put(checksumKind, new FileChecksum(split, checksumKind, digests[i])); + } + } + } catch (IOException | DigestException e) { + Slog.e(TAG, "Error computing hash.", e); + } + } +} diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 4f3d3600ae37..ed62362b04fb 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -134,6 +134,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.dex.DexManager; @@ -1554,12 +1555,28 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } private void onSessionValidationFailure(int error, String detailMessage) { - // Session is sealed but could not be verified, we need to destroy it. + // Session is sealed but could not be validated, we need to destroy it. destroyInternal(); // Dispatch message to remove session from PackageInstallerService. dispatchSessionFinished(error, detailMessage, null); } + private void onSessionVerificationFailure(int error, String detailMessage) { + Slog.e(TAG, "Failed to verify session " + sessionId + " [" + detailMessage + "]"); + // Session is sealed and committed but could not be verified, we need to destroy it. + destroyInternal(); + if (isStaged()) { + setStagedSessionFailed( + SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, detailMessage); + // TODO(b/136257624): Remove this once all verification logic has been transferred out + // of StagingManager. + mStagingManager.notifyVerificationComplete(sessionId); + } else { + // Dispatch message to remove session from PackageInstallerService. + dispatchSessionFinished(error, detailMessage, null); + } + } + private void onStorageUnhealthy() { final String packageName = getPackageName(); if (TextUtils.isEmpty(packageName)) { @@ -1680,7 +1697,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } if (params.isStaged) { mStagingManager.commitSession(this); - destroyInternal(); + // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even + // though ideally, we just need to send session committed broadcast. dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null); return; } @@ -1691,14 +1709,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "APEX packages can only be installed using staged sessions.", null); return; } + verify(); + } + /** + * Resumes verification process for non-final committed staged session. + * + * Useful if a device gets rebooted before verification is complete and we need to restart the + * verification. + */ + void verifyStagedSession() { + assertCallerIsOwnerOrRootOrSystemLocked(); + Preconditions.checkArgument(isCommitted()); + Preconditions.checkArgument(isStaged()); + Preconditions.checkArgument(!mStagedSessionApplied && !mStagedSessionFailed); + + verify(); + } + + private void verify() { try { verifyNonStaged(); } catch (PackageManagerException e) { final String completeMsg = ExceptionUtils.getCompleteMessage(e); - Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); - destroyInternal(); - dispatchSessionFinished(e.error, completeMsg, null); + onSessionVerificationFailure(e.error, completeMsg); } } @@ -1846,7 +1880,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private PackageManagerService.VerificationParams makeVerificationParamsLocked() throws PackageManagerException { - if (!params.isMultiPackage) { + // TODO(b/136257624): Some logic in this if block probably belongs in + // makeInstallParams(). + if (!params.isMultiPackage && !isApexInstallation()) { Objects.requireNonNull(mPackageName); Objects.requireNonNull(mSigningDetails); Objects.requireNonNull(mResolvedBaseFile); @@ -1923,8 +1959,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (returnCode == PackageManager.INSTALL_SUCCEEDED) { onVerificationComplete(); } else { - destroyInternal(); - dispatchSessionFinished(returnCode, msg, extras); + onSessionVerificationFailure(returnCode, msg); } } }; @@ -1946,9 +1981,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } private void onVerificationComplete() { - if ((params.installFlags & PackageManager.INSTALL_DRY_RUN) != 0) { - destroyInternal(); - dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Dry run", new Bundle()); + // Staged sessions will be installed later during boot + if (isStaged()) { + // TODO(b/136257624): Remove this once all verification logic has been transferred out + // of StagingManager. + mStagingManager.notifyPreRebootVerification_Apk_Complete(sessionId); + // TODO(b/136257624): We also need to destroy internals for verified staged session, + // otherwise file descriptors are never closed for verified staged session until reboot return; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 5fd73743c3ac..344f9cfdbc96 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -40,6 +40,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import static android.content.pm.PackageManager.EXTRA_CHECKSUMS; import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; @@ -168,6 +169,7 @@ import android.content.pm.ComponentInfo; import android.content.pm.DataLoaderType; import android.content.pm.FallbackCategoryProvider; import android.content.pm.FeatureInfo; +import android.content.pm.FileChecksum; import android.content.pm.IDexModuleRegisterCallback; import android.content.pm.IPackageChangeObserver; import android.content.pm.IPackageDataObserver; @@ -254,6 +256,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Parcel; +import android.os.ParcelableException; import android.os.PatternMatcher; import android.os.PersistableBundle; import android.os.Process; @@ -394,6 +397,7 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -404,7 +408,10 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.SecureRandom; +import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -2400,9 +2407,12 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUserId = UserHandle.getUserId(callingUid); for (String packageName : packages) { - PackageSetting setting = mSettings.mPackages.get(packageName); - if (setting != null - && !shouldFilterApplicationLocked(setting, callingUid, callingUserId)) { + final boolean filterApp; + synchronized (mLock) { + final PackageSetting ps = mSettings.getPackageLPr(packageName); + filterApp = shouldFilterApplicationLocked(ps, callingUid, callingUserId); + } + if (!filterApp) { notifyInstallObserver(packageName); } } @@ -2443,6 +2453,83 @@ public class PackageManagerService extends IPackageManager.Stub mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS); } + @Override + public void getChecksums(@NonNull String packageName, boolean includeSplits, + @PackageManager.FileChecksumKind int optional, + @PackageManager.FileChecksumKind int required, @Nullable List trustedInstallers, + @NonNull IntentSender statusReceiver, int userId) { + Objects.requireNonNull(packageName); + Objects.requireNonNull(statusReceiver); + + final ApplicationInfo applicationInfo = getApplicationInfoInternal(packageName, 0, + Binder.getCallingUid(), userId); + if (applicationInfo == null) { + throw new ParcelableException(new PackageManager.NameNotFoundException(packageName)); + } + + List<Pair<String, File>> filesToChecksum = new ArrayList<>(); + + // Adding base split. + filesToChecksum.add(Pair.create(null, new File(applicationInfo.sourceDir))); + + // Adding other splits. + if (includeSplits && applicationInfo.splitNames != null) { + for (int i = 0, size = applicationInfo.splitNames.length; i < size; ++i) { + filesToChecksum.add(Pair.create(applicationInfo.splitNames[i], + new File(applicationInfo.splitSourceDirs[i]))); + } + } + + for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { + final File file = filesToChecksum.get(i).second; + if (!file.exists()) { + throw new IllegalStateException("File not found: " + file.getPath()); + } + } + + final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates( + trustedInstallers) : null; + final Context context = mContext; + + mInjector.getBackgroundExecutor().execute(() -> { + final Intent intent = new Intent(); + List<FileChecksum> result = new ArrayList<>(); + for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { + final String split = filesToChecksum.get(i).first; + final File file = filesToChecksum.get(i).second; + try { + result.addAll(ApkChecksums.getFileChecksums(split, file, optional, required, + trustedCerts)); + } catch (Throwable e) { + Slog.e(TAG, "Checksum calculation error", e); + } + } + intent.putExtra(EXTRA_CHECKSUMS, + result.toArray(new FileChecksum[result.size()])); + + try { + statusReceiver.sendIntent(context, 1, intent, null, null); + } catch (SendIntentException e) { + Slog.w(TAG, e); + } + }); + } + + private static @NonNull Certificate[] decodeCertificates(@NonNull List certs) { + try { + final CertificateFactory cf = CertificateFactory.getInstance("X.509"); + final Certificate[] result = new Certificate[certs.size()]; + for (int i = 0, size = certs.size(); i < size; ++i) { + final InputStream is = new ByteArrayInputStream((byte[]) certs.get(i)); + final X509Certificate cert = (X509Certificate) cf.generateCertificate(is); + result[i] = cert; + } + return result; + } catch (CertificateException e) { + throw ExceptionUtils.propagate(e); + } + } + /** * Gets the type of the external storage a package is installed on. * @param packageVolume The storage volume of the package. @@ -8964,10 +9051,10 @@ public class PackageManagerService extends IPackageManager.Stub if (providerInfo == null) { return null; } - if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { - return null; - } synchronized (mLock) { + if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { + return null; + } final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); @@ -9054,9 +9141,11 @@ public class PackageManagerService extends IPackageManager.Stub String targetPackage, int flags) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); - final PackageSetting ps = mSettings.mPackages.get(targetPackage); - if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { - return ParceledListSlice.emptyList(); + synchronized (mLock) { + final PackageSetting ps = mSettings.getPackageLPr(targetPackage); + if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { + return ParceledListSlice.emptyList(); + } } return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags, callingUserId)); @@ -11587,7 +11676,7 @@ public class PackageManagerService extends IPackageManager.Stub configurePackageComponents(parsedPackage); } - final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride, pkgSetting); + final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride); final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp(); if ((scanFlags & SCAN_NEW_INSTALL) == 0) { @@ -13793,7 +13882,7 @@ public class PackageManagerService extends IPackageManager.Stub final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId); final long callingId = Binder.clearCallingIdentity(); try { - final String activeLauncherPackageName = getActiveLauncherPackageName(userId); + final String activeLauncherPackageName = mPermissionManager.getDefaultHome(userId); final String dialerPackageName = mPermissionManager.getDefaultDialer(userId); for (int i = 0; i < packageNames.length; i++) { canSuspend[i] = false; @@ -13869,18 +13958,6 @@ public class PackageManagerService extends IPackageManager.Stub return canSuspend; } - private String getActiveLauncherPackageName(int userId) { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.addCategory(Intent.CATEGORY_HOME); - ResolveInfo resolveInfo = resolveIntent( - intent, - intent.resolveTypeIfNeeded(mContext.getContentResolver()), - PackageManager.MATCH_DEFAULT_ONLY, - userId); - - return resolveInfo == null ? null : resolveInfo.activityInfo.packageName; - } - @Override public void verifyPendingInstall(int id, int verificationCode) throws RemoteException { mContext.enforceCallingOrSelfPermission( @@ -14572,7 +14649,7 @@ public class PackageManagerService extends IPackageManager.Stub final PackageSetting ps; int appId = -1; long ceDataInode = -1; - synchronized (mSettings) { + synchronized (mLock) { ps = mSettings.getPackageLPr(packageName); if (ps != null) { appId = ps.appId; @@ -15201,6 +15278,13 @@ public class PackageManagerService extends IPackageManager.Stub } public void handleStartCopy() { + if ((installFlags & PackageManager.INSTALL_APEX) != 0) { + // Apex packages get verified in StagingManager currently. + // TODO(b/136257624): Move apex verification logic out of StagingManager + mRet = INSTALL_SUCCEEDED; + return; + } + PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext, origin.resolvedPath, installFlags, packageAbiOverride); @@ -17613,7 +17697,7 @@ public class PackageManagerService extends IPackageManager.Stub } boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null && pkgSetting.getPkgState().isUpdatedSystemApp(); - final String abiOverride = deriveAbiOverride(args.abiOverride, pkgSetting); + final String abiOverride = deriveAbiOverride(args.abiOverride); AndroidPackage oldPackage = mPackages.get(pkgName); boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem(); final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> @@ -19446,6 +19530,9 @@ public class PackageManagerService extends IPackageManager.Stub } if (outInfo != null) { + if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { + outInfo.dataRemoved = true; + } outInfo.removedPackage = ps.name; outInfo.installerPackageName = ps.installSource.installerPackageName; outInfo.isStaticSharedLib = pkg != null && pkg.getStaticSharedLibName() != null; @@ -19481,9 +19568,11 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "clear application data"); - final PackageSetting ps = mSettings.getPackageLPr(packageName); - final boolean filterApp = - (ps != null && shouldFilterApplicationLocked(ps, callingUid, userId)); + final boolean filterApp; + synchronized (mLock) { + final PackageSetting ps = mSettings.getPackageLPr(packageName); + filterApp = shouldFilterApplicationLocked(ps, callingUid, userId); + } if (!filterApp && mProtectedPackages.isPackageDataProtected(userId, packageName)) { throw new SecurityException("Cannot clear data for a protected package: " + packageName); @@ -19763,11 +19852,13 @@ public class PackageManagerService extends IPackageManager.Stub if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) != PackageManager.PERMISSION_GRANTED) { - if (getUidTargetSdkVersionLockedLPr(callingUid) - < Build.VERSION_CODES.FROYO) { - Slog.w(TAG, "Ignoring addPreferredActivity() from uid " - + callingUid); - return; + synchronized (mLock) { + if (getUidTargetSdkVersionLockedLPr(callingUid) + < Build.VERSION_CODES.FROYO) { + Slog.w(TAG, "Ignoring addPreferredActivity() from uid " + + callingUid); + return; + } } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); @@ -19947,8 +20038,9 @@ public class PackageManagerService extends IPackageManager.Stub /** This method takes a specific user id as well as UserHandle.USER_ALL. */ private void clearPackagePreferredActivities(String packageName, int userId) { final SparseBooleanArray changedUsers = new SparseBooleanArray(); - - clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId); + synchronized (mLock) { + clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId); + } if (changedUsers.size() > 0) { updateDefaultHomeNotLocked(changedUsers); postPreferredActivityChangedBroadcast(userId); @@ -20070,7 +20162,9 @@ public class PackageManagerService extends IPackageManager.Stub // writer try { final SparseBooleanArray changedUsers = new SparseBooleanArray(); - clearPackagePreferredActivitiesLPw(null, changedUsers, userId); + synchronized (mLock) { + clearPackagePreferredActivitiesLPw(null, changedUsers, userId); + } if (changedUsers.size() > 0) { postPreferredActivityChangedBroadcast(userId); } @@ -20538,6 +20632,9 @@ public class PackageManagerService extends IPackageManager.Stub if (cn != null) { return cn; } + // TODO: This should not happen since there should always be a default package set for + // ROLE_HOME in RoleManager. Continue with a warning log for now. + Slog.w(TAG, "Default package for ROLE_HOME is not set in RoleManager"); // Find the launcher with the highest priority and return that component if there are no // other home activity with the same priority. @@ -20586,6 +20683,7 @@ public class PackageManagerService extends IPackageManager.Stub if (packageName == null) { return null; } + int resolveInfosSize = resolveInfos.size(); for (int i = 0; i < resolveInfosSize; i++) { ResolveInfo resolveInfo = resolveInfos.get(i); @@ -20645,6 +20743,11 @@ public class PackageManagerService extends IPackageManager.Stub // PermissionController manages default home directly. return false; } + + if (packageName == null) { + // Keep the default home package in RoleManager. + return false; + } mPermissionManager.setDefaultHome(packageName, userId, (successful) -> { if (successful) { postPreferredActivityChangedBroadcast(userId); @@ -21080,15 +21183,19 @@ public class PackageManagerService extends IPackageManager.Stub // Limit who can change which apps if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) { // Don't allow apps that don't have permission to modify other apps - if (!allowedByPermission - || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) { + final boolean filterApp; + synchronized (mLock) { + filterApp = (!allowedByPermission + || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)); + } + if (filterApp) { throw new SecurityException( "Attempt to change component state; " - + "pid=" + Binder.getCallingPid() - + ", uid=" + callingUid - + (className == null + + "pid=" + Binder.getCallingPid() + + ", uid=" + callingUid + + (className == null ? ", package=" + packageName - : ", component=" + packageName + "/" + className)); + : ", component=" + packageName + "/" + className)); } // Don't allow changing protected packages. if (mProtectedPackages.isPackageStateProtected(userId, packageName)) { diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 491b4fc515ce..5553cd0e2fb8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -421,18 +421,13 @@ public class PackageManagerServiceUtils { /** * Derive the value of the {@code cpuAbiOverride} based on the provided - * value and an optional stored value from the package settings. + * value. */ - public static String deriveAbiOverride(String abiOverride, PackageSetting settings) { - String cpuAbiOverride = null; + public static String deriveAbiOverride(String abiOverride) { if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) { - cpuAbiOverride = null; - } else if (abiOverride != null) { - cpuAbiOverride = abiOverride; - } else if (settings != null) { - cpuAbiOverride = settings.cpuAbiOverrideString; + return null; } - return cpuAbiOverride; + return abiOverride; } /** diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 7e2ef41943d6..4d2e22a2640e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -103,6 +103,7 @@ import android.util.SparseArray; import com.android.internal.content.PackageHelper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata; @@ -138,7 +139,7 @@ class PackageManagerShellCommand extends ShellCommand { private static final String STDIN_PATH = "-"; /** Path where ART profiles snapshots are dumped for the shell user */ private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/"; - private static final int DEFAULT_WAIT_MS = 60 * 1000; + private static final int DEFAULT_STAGED_READY_TIMEOUT_MS = 60 * 1000; private static final String TAG = "PackageManagerShellCommand"; final IPackageManager mInterface; @@ -463,9 +464,20 @@ class PackageManagerShellCommand extends ShellCommand { return 1; } - private int runRollbackApp() { + private int runRollbackApp() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); + String opt; + long stagedReadyTimeoutMs = DEFAULT_STAGED_READY_TIMEOUT_MS; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "--staged-ready-timeout": + stagedReadyTimeoutMs = Long.parseLong(getNextArgRequired()); + break; + default: + throw new IllegalArgumentException("Unknown option: " + opt); + } + } final String packageName = getNextArgRequired(); if (packageName == null) { pw.println("Error: package name not specified"); @@ -495,14 +507,21 @@ class PackageManagerShellCommand extends ShellCommand { final Intent result = receiver.getResult(); final int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, RollbackManager.STATUS_FAILURE); - if (status == RollbackManager.STATUS_SUCCESS) { - pw.println("Success"); - return 0; - } else { + + if (status != RollbackManager.STATUS_SUCCESS) { pw.println("Failure [" + result.getStringExtra(RollbackManager.EXTRA_STATUS_MESSAGE) + "]"); return 1; } + + if (rollback.isStaged() && stagedReadyTimeoutMs > 0) { + final int committedSessionId = rollback.getCommittedSessionId(); + return doWaitForStagedSessionReady(committedSessionId, stagedReadyTimeoutMs, pw); + } + + pw.println("Success"); + return 0; + } private void setParamsSize(InstallParams params, List<String> inPaths) { @@ -1307,11 +1326,12 @@ class PackageManagerShellCommand extends ShellCommand { } abandonSession = false; - if (!params.sessionParams.isStaged || !params.mWaitForStagedSessionReady) { - pw.println("Success"); - return 0; + if (params.sessionParams.isStaged && params.stagedReadyTimeoutMs > 0) { + return doWaitForStagedSessionReady(sessionId, params.stagedReadyTimeoutMs, pw); } - return doWaitForStagedSessionRead(sessionId, params.timeoutMs, pw); + + pw.println("Success"); + return 0; } finally { if (abandonSession) { try { @@ -1322,11 +1342,9 @@ class PackageManagerShellCommand extends ShellCommand { } } - private int doWaitForStagedSessionRead(int sessionId, long timeoutMs, PrintWriter pw) + private int doWaitForStagedSessionReady(int sessionId, long timeoutMs, PrintWriter pw) throws RemoteException { - if (timeoutMs <= 0) { - timeoutMs = DEFAULT_WAIT_MS; - } + Preconditions.checkArgument(timeoutMs > 0); PackageInstaller.SessionInfo si = mInterface.getPackageInstaller() .getSessionInfo(sessionId); if (si == null) { @@ -1376,25 +1394,14 @@ class PackageManagerShellCommand extends ShellCommand { private int runInstallCommit() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); String opt; - boolean waitForStagedSessionReady = true; - long timeoutMs = -1; + long stagedReadyTimeoutMs = DEFAULT_STAGED_READY_TIMEOUT_MS; while ((opt = getNextOption()) != null) { switch (opt) { - case "--wait-for-staged-ready": - waitForStagedSessionReady = true; - // If there is only one remaining argument, then it represents the sessionId, we - // shouldn't try to parse it as timeoutMs. - if (getRemainingArgsCount() > 1) { - try { - timeoutMs = Long.parseLong(peekNextArg()); - getNextArg(); - } catch (NumberFormatException ignore) { - } - } - break; - case "--no-wait": - waitForStagedSessionReady = false; + case "--staged-ready-timeout": + stagedReadyTimeoutMs = Long.parseLong(getNextArgRequired()); break; + default: + throw new IllegalArgumentException("Unknown option: " + opt); } } final int sessionId = Integer.parseInt(getNextArg()); @@ -1403,11 +1410,11 @@ class PackageManagerShellCommand extends ShellCommand { } final PackageInstaller.SessionInfo si = mInterface.getPackageInstaller() .getSessionInfo(sessionId); - if (si == null || !si.isStaged() || !waitForStagedSessionReady) { - pw.println("Success"); - return 0; + if (si != null && si.isStaged() && stagedReadyTimeoutMs > 0) { + return doWaitForStagedSessionReady(sessionId, stagedReadyTimeoutMs, pw); } - return doWaitForStagedSessionRead(sessionId, timeoutMs, pw); + pw.println("Success"); + return 0; } private int runInstallCreate() throws RemoteException { @@ -2738,8 +2745,7 @@ class PackageManagerShellCommand extends ShellCommand { SessionParams sessionParams; String installerPackageName; int userId = UserHandle.USER_ALL; - boolean mWaitForStagedSessionReady = true; - long timeoutMs = DEFAULT_WAIT_MS; + long stagedReadyTimeoutMs = DEFAULT_STAGED_READY_TIMEOUT_MS; } private InstallParams makeInstallParams() { @@ -2868,16 +2874,8 @@ class PackageManagerShellCommand extends ShellCommand { } sessionParams.installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK; break; - case "--wait-for-staged-ready": - params.mWaitForStagedSessionReady = true; - try { - params.timeoutMs = Long.parseLong(peekNextArg()); - getNextArg(); - } catch (NumberFormatException ignore) { - } - break; - case "--no-wait": - params.mWaitForStagedSessionReady = false; + case "--staged-ready-timeout": + params.stagedReadyTimeoutMs = Long.parseLong(getNextArgRequired()); break; case "--skip-verification": sessionParams.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; @@ -3613,7 +3611,7 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" [--preload] [--instant] [--full] [--dont-kill]"); pw.println(" [--enable-rollback]"); pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]"); - pw.println(" [--apex] [--wait-for-staged-ready TIMEOUT]"); + pw.println(" [--apex] [--staged-ready-timeout TIMEOUT]"); pw.println(" [PATH [SPLIT...]|-]"); pw.println(" Install an application. Must provide the apk data to install, either as"); pw.println(" file path(s) or '-' to read from stdin. Options are:"); @@ -3641,9 +3639,11 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" 3=device setup, 4=user request"); pw.println(" --force-uuid: force install on to disk volume with given UUID"); pw.println(" --apex: install an .apex file, not an .apk"); - pw.println(" --wait-for-staged-ready: when performing staged install, wait TIMEOUT"); - pw.println(" ms for pre-reboot verification to complete. If TIMEOUT is not"); - pw.println(" specified it will wait for " + DEFAULT_WAIT_MS + " milliseconds."); + pw.println(" --staged-ready-timeout: By default, staged sessions wait " + + DEFAULT_STAGED_READY_TIMEOUT_MS); + pw.println(" milliseconds for pre-reboot verification to complete when"); + pw.println(" performing staged install. This flag is used to alter the waiting"); + pw.println(" time. You can skip the waiting time by specifying a TIMEOUT of '0'"); pw.println(""); pw.println(" install-existing [--user USER_ID|all|current]"); pw.println(" [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE"); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 105a9b06cf42..0c4eaec32ba5 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -88,7 +88,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; @@ -189,7 +188,6 @@ public class StagingManager { * Validates the signature used to sign the container of the new apex package * * @param newApexPkg The new apex package that is being installed - * @throws PackageManagerException */ private void validateApexSignature(PackageInfo newApexPkg) throws PackageManagerException { @@ -725,12 +723,9 @@ public class StagingManager { return ret; } - @NonNull private PackageInstallerSession createAndWriteApkSession( - @NonNull PackageInstallerSession originalSession, boolean preReboot) - throws PackageManagerException { - final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED - : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED; + PackageInstallerSession originalSession) throws PackageManagerException { + final int errorCode = SessionInfo.STAGED_SESSION_ACTIVATION_FAILED; if (originalSession.stageDir == null) { Slog.wtf(TAG, "Attempting to install a staged APK session with no staging dir"); throw new PackageManagerException(errorCode, @@ -746,12 +741,7 @@ public class StagingManager { PackageInstaller.SessionParams params = originalSession.params.copy(); params.isStaged = false; params.installFlags |= PackageManager.INSTALL_STAGED; - if (preReboot) { - params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK; - params.installFlags |= PackageManager.INSTALL_DRY_RUN; - } else { - params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; - } + params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; try { int apkSessionId = mPi.createSession( params, originalSession.getInstallerPackageName(), @@ -783,12 +773,10 @@ public class StagingManager { * apks in the given session. Only parent session is returned for multi-package session. */ @Nullable - private PackageInstallerSession extractApksInSession(PackageInstallerSession session, - boolean preReboot) throws PackageManagerException { - final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED - : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED; + private PackageInstallerSession extractApksInSession(PackageInstallerSession session) + throws PackageManagerException { if (!session.isMultiPackage() && !isApexSession(session)) { - return createAndWriteApkSession(session, preReboot); + return createAndWriteApkSession(session); } else if (session.isMultiPackage()) { // For multi-package staged sessions containing APKs, we identify which child sessions // contain an APK, and with those then create a new multi-package group of sessions, @@ -810,10 +798,6 @@ public class StagingManager { } final PackageInstaller.SessionParams params = session.params.copy(); params.isStaged = false; - if (preReboot) { - params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK; - params.installFlags |= PackageManager.INSTALL_DRY_RUN; - } final int apkParentSessionId = mPi.createSession( params, session.getInstallerPackageName(), session.getInstallerAttributionTag(), session.userId); @@ -823,18 +807,18 @@ public class StagingManager { } catch (IOException e) { Slog.e(TAG, "Unable to prepare multi-package session for staged session " + session.sessionId); - throw new PackageManagerException(errorCode, + throw new PackageManagerException(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "Unable to prepare multi-package session for staged session"); } for (int i = 0, size = childSessions.size(); i < size; i++) { final PackageInstallerSession apkChildSession = createAndWriteApkSession( - childSessions.get(i), preReboot); + childSessions.get(i)); try { apkParentSession.addChildSessionId(apkChildSession.sessionId); } catch (IllegalStateException e) { Slog.e(TAG, "Failed to add a child session for installing the APK files", e); - throw new PackageManagerException(errorCode, + throw new PackageManagerException(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "Failed to add a child session " + apkChildSession.sessionId); } } @@ -877,11 +861,9 @@ public class StagingManager { } } - private void installApksInSession(@NonNull PackageInstallerSession session) + private void installApksInSession(PackageInstallerSession session) throws PackageManagerException { - - final PackageInstallerSession apksToInstall = extractApksInSession( - session, /* preReboot */ false); + final PackageInstallerSession apksToInstall = extractApksInSession(session); if (apksToInstall == null) { return; } @@ -1047,7 +1029,7 @@ public class StagingManager { /** * <p>Abort committed staged session * - * <p>This method must be called while holding {@link PackageInstallerSession.mLock}. + * <p>This method must be called while holding {@link PackageInstallerSession#mLock}. * * <p>The method returns {@code false} to indicate it is not safe to clean up the session from * system yet. When it is safe, the method returns {@code true}. @@ -1218,7 +1200,7 @@ public class StagingManager { } } - void markStagedSessionsAsSuccessful() { + private void markStagedSessionsAsSuccessful() { synchronized (mSuccessfulStagedSessionIds) { for (int i = 0; i < mSuccessfulStagedSessionIds.size(); i++) { mApexManager.markStagedSessionSuccessful(mSuccessfulStagedSessionIds.get(i)); @@ -1242,30 +1224,10 @@ public class StagingManager { mFailureReasonFile.delete(); } - private static class LocalIntentReceiverAsync { - final Consumer<Intent> mConsumer; - - LocalIntentReceiverAsync(Consumer<Intent> consumer) { - mConsumer = consumer; - } - - private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { - @Override - public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, - IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { - mConsumer.accept(intent); - } - }; - - public IntentSender getIntentSender() { - return new IntentSender((IIntentSender) mLocalSender); - } - } - private static class LocalIntentReceiverSync { private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>(); - private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { + private final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { @Override public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, IIntentReceiver finishedReceiver, String requiredPermission, @@ -1299,6 +1261,20 @@ public class StagingManager { return session; } + // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all + // verification logic is extracted out of StagingManager into PMS, we can remove + // this. + void notifyVerificationComplete(int sessionId) { + mPreRebootVerificationHandler.onPreRebootVerificationComplete(sessionId); + } + + // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all + // verification logic is extracted out of StagingManager into PMS, we can remove + // this. + void notifyPreRebootVerification_Apk_Complete(int sessionId) { + mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(sessionId); + } + private final class PreRebootVerificationHandler extends Handler { // Hold session ids before handler gets ready to do the verification. private IntArray mPendingSessionIds; @@ -1500,54 +1476,16 @@ public class StagingManager { } /** - * Pre-reboot verification state for apk files: - * <p><ul> - * <li>performs a dry-run install of apk</li> - * </ul></p> + * Pre-reboot verification state for apk files. Session is sent to + * {@link PackageManagerService} for verification and it notifies back the result via + * {@link #notifyPreRebootVerification_Apk_Complete(int)} */ private void handlePreRebootVerification_Apk(@NonNull PackageInstallerSession session) { if (!sessionContainsApk(session)) { notifyPreRebootVerification_Apk_Complete(session.sessionId); return; } - - try { - Slog.d(TAG, "Running a pre-reboot verification for APKs in session " - + session.sessionId + " by performing a dry-run install"); - // verifyApksInSession will notify the handler when APK verification is complete - verifyApksInSession(session); - } catch (PackageManagerException e) { - onPreRebootVerificationFailure(session, e.error, e.getMessage()); - } - } - - private void verifyApksInSession(PackageInstallerSession session) - throws PackageManagerException { - - final PackageInstallerSession apksToVerify = extractApksInSession( - session, /* preReboot */ true); - if (apksToVerify == null) { - return; - } - - final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync( - (Intent result) -> { - final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE); - if (status != PackageInstaller.STATUS_SUCCESS) { - final String errorMessage = result.getStringExtra( - PackageInstaller.EXTRA_STATUS_MESSAGE); - Slog.e(TAG, "Failure to verify APK staged session " - + session.sessionId + " [" + errorMessage + "]"); - onPreRebootVerificationFailure(session, - SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage); - return; - } - mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete( - session.sessionId); - }); - - apksToVerify.commit(receiver.getIntentSender(), false); + session.verifyStagedSession(); } /** diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index c1aebd33889c..d137fd05f793 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -84,7 +84,6 @@ import android.os.storage.StorageManager; import android.security.GateKeeper; import android.service.gatekeeper.IGateKeeperService; import android.stats.devicepolicy.DevicePolicyEnums; -import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; @@ -142,6 +141,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicInteger; /** * Service for {@link UserManager}. @@ -159,10 +159,6 @@ public class UserManagerService extends IUserManager.Stub { private static final String LOG_TAG = "UserManagerService"; static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE - - // TODO(b/164159026): remove once owner_name issue on automotive is fixed - // Can be used to track getUsers() / userWithNameLU() behavior - public static final boolean DBG_CACHED_USERINFOS = false; // DO NOT SUBMIT WITH TRUE // Can be used for manual testing of id recycling private static final boolean RELEASE_DELETED_USER_ID = false; // DO NOT SUBMIT WITH TRUE @@ -276,25 +272,6 @@ public class UserManagerService extends IUserManager.Stub { private DevicePolicyManagerInternal mDevicePolicyManagerInternal; /** - * Reference to the {@link UserHandle#SYSTEM} user's UserInfo; it's {@code name} was either - * manually set, or it's {@code null}. - * - * <p>The reference is set just once, but it's {@code name} is updated when it's manually set. - */ - @GuardedBy("mUsersLock") - private UserInfo mSystemUserInfo; - - /** - * Reference to the {@link UserHandle#SYSTEM} user's UserInfo, with its {@code name} set to - * the localized value of {@code owner_name}. - * - * <p>The reference is set just once, but it's {@code name} is updated everytime the reference - * is used and the locale changed. - */ - @GuardedBy("mUsersLock") - private UserInfo mSystemUserInfoWithName; - - /** * Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps. */ @VisibleForTesting @@ -467,6 +444,11 @@ public class UserManagerService extends IUserManager.Stub { } }; + // TODO(b/161915546): remove once userWithName() is fixed / removed + // Use to debug / dump when user 0 is allocated at userWithName() + public static final boolean DBG_ALLOCATION = false; // DO NOT SUBMIT WITH TRUE + public final AtomicInteger mUser0Allocations; + /** * Start an {@link IntentSender} when user is unlocked after disabling quiet mode. * @@ -656,6 +638,7 @@ public class UserManagerService extends IUserManager.Stub { LocalServices.addService(UserManagerInternal.class, mLocalService); mLockPatternUtils = new LockPatternUtils(mContext); mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING); + mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null; } void systemReady() { @@ -801,7 +784,7 @@ public class UserManagerService extends IUserManager.Stub { || (excludePreCreated && ui.preCreated)) { continue; } - users.add(userWithNameLU(ui)); + users.add(userWithName(ui)); } return users; } @@ -870,7 +853,7 @@ public class UserManagerService extends IUserManager.Stub { userInfo.name = null; userInfo.iconPath = null; } else { - userInfo = userWithNameLU(userInfo); + userInfo = userWithName(userInfo); } users.add(userInfo); } @@ -1327,57 +1310,26 @@ public class UserManagerService extends IUserManager.Stub { public UserInfo getUserInfo(@UserIdInt int userId) { checkManageOrCreateUsersPermission("query user"); synchronized (mUsersLock) { - return userWithNameLU(getUserInfoLU(userId)); + return userWithName(getUserInfoLU(userId)); } } /** * Returns a UserInfo object with the name filled in, for Owner, or the original * if the name is already set. - * - * <p>Note:</p> the Owner name is localized, so the current value must be checked every time - * this method is called. */ - private UserInfo userWithNameLU(UserInfo orig) { - // Only the system user uses the owner_name string. - if (orig == null || orig.id != UserHandle.USER_SYSTEM) return orig; - - if (mSystemUserInfo == null) { - mSystemUserInfo = orig; - if (DBG_CACHED_USERINFOS) { - Slog.d(LOG_TAG, "Set mSystemUserInfo:" + mSystemUserInfo.toFullString()); - } - } - - if (mSystemUserInfo.name != null) { - if (DBG_CACHED_USERINFOS) { - Slog.v(LOG_TAG, "Returning mSystemUserInfo: " + mSystemUserInfo.toFullString()); - } - return mSystemUserInfo; - } - - final String ownerName = getOwnerName(); - - if (mSystemUserInfoWithName == null) { - mSystemUserInfoWithName = new UserInfo(orig); - mSystemUserInfoWithName.name = ownerName; - if (DBG_CACHED_USERINFOS) { - Slog.d(LOG_TAG, "Set mSystemUserInfoWithName: " - + mSystemUserInfoWithName.toFullString()); - } - } else if (!TextUtils.equals(ownerName, mSystemUserInfoWithName.name)) { - if (DBG_CACHED_USERINFOS) { - Slog.d(LOG_TAG, "Updating mSystemUserInfoWithName.name from " - + mSystemUserInfoWithName.name + " to " + ownerName); - } - mSystemUserInfoWithName.name = ownerName; - } - - if (DBG_CACHED_USERINFOS) { - Slog.v(LOG_TAG, "Returning mSystemUserInfoWithName:" - + mSystemUserInfoWithName.toFullString()); + private UserInfo userWithName(UserInfo orig) { + if (orig != null && orig.name == null && orig.id == UserHandle.USER_SYSTEM) { + if (DBG_ALLOCATION) { + final int number = mUser0Allocations.incrementAndGet(); + Slog.w(LOG_TAG, "System user instantiated at least " + number + " times"); + } + UserInfo withName = new UserInfo(orig); + withName.name = getOwnerName(); + return withName; + } else { + return orig; } - return mSystemUserInfoWithName; } /** Returns whether the given user type is one of the FULL user types. */ @@ -1530,7 +1482,7 @@ public class UserManagerService extends IUserManager.Stub { } final int userId = UserHandle.getUserId(Binder.getCallingUid()); synchronized (mUsersLock) { - UserInfo userInfo = userWithNameLU(getUserInfoLU(userId)); + UserInfo userInfo = userWithName(getUserInfoLU(userId)); return userInfo == null ? "" : userInfo.name; } } @@ -1645,13 +1597,6 @@ public class UserManagerService extends IUserManager.Stub { Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId); return null; } - - if (DBG_CACHED_USERINFOS && userId == UserHandle.USER_SYSTEM && userData != null - && userData.info != mSystemUserInfo) { - Slog.wtf(LOG_TAG, "getUserInfoLU(): system user on userData (" + userData.info - + ") is not the same as mSystemUserInfo (" + mSystemUserInfo + ")"); - } - return userData != null ? userData.info : null; } @@ -4910,15 +4855,8 @@ public class UserManagerService extends IUserManager.Stub { pw.println(" Is headless-system mode: " + UserManager.isHeadlessSystemUserMode()); pw.println(" User version: " + mUserVersion); pw.println(" Owner name: " + getOwnerName()); - if (mSystemUserInfo == null) { - pw.println(" (mSystemUserInfo not set)"); - } else { - pw.println(" System user: " + mSystemUserInfo.toFullString()); - } - if (mSystemUserInfoWithName == null) { - pw.println(" (mSystemUserInfoWithName not set)"); - } else { - pw.println(" System user (with name): " + mSystemUserInfoWithName.toFullString()); + if (DBG_ALLOCATION) { + pw.println(" System user allocations: " + mUser0Allocations.get()); } // Dump UserTypes diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 548cd70de4d1..d01a30fbd818 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -69,6 +69,7 @@ import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; @@ -148,6 +149,7 @@ import android.os.UEventObserver; import android.os.UserHandle; import android.os.VibrationEffect; import android.os.Vibrator; +import android.provider.DeviceConfig; import android.provider.MediaStore; import android.provider.Settings; import android.service.dreams.DreamManagerInternal; @@ -1378,12 +1380,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private long getScreenshotChordLongPressDelay() { + long delayMs = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_SYSTEMUI, SCREENSHOT_KEYCHORD_DELAY, + ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout()); if (mKeyguardDelegate.isShowing()) { // Double the time it takes to take a screenshot from the keyguard - return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * - ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout()); + return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * delayMs); } - return ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout(); + return delayMs; } private long getRingerToggleChordDelay() { @@ -2219,11 +2223,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public int getMaxWallpaperLayer() { - return getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE); - } - - @Override public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) { return attrs.type == TYPE_NOTIFICATION_SHADE; } @@ -5324,15 +5323,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public boolean isTopLevelWindow(int windowType) { - if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW - && windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { - return (windowType == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG); - } - return true; - } - - @Override public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode()); diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 651eafd77fe7..b96d65cb7433 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -67,7 +67,6 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.WindowConfiguration; import android.content.Context; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -90,7 +89,6 @@ import android.view.animation.Animation; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IShortcutService; import com.android.server.wm.DisplayRotation; -import com.android.server.wm.WindowFrames; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -181,92 +179,11 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { */ public interface WindowState { /** - * Return the uid of the app that owns this window. - */ - int getOwningUid(); - - /** * Return the package name of the app that owns this window. */ String getOwningPackage(); /** - * Perform standard frame computation. The result can be obtained with - * getFrame() if so desired. Must be called with the window manager - * lock held. - * - */ - public void computeFrameLw(); - - /** - * Retrieve the current frame of the window that has been assigned by - * the window manager. Must be called with the window manager lock held. - * - * @return Rect The rectangle holding the window frame. - */ - public Rect getFrameLw(); - - /** - * Retrieve the frame of the display that this window was last - * laid out in. Must be called with the - * window manager lock held. - * - * @return Rect The rectangle holding the display frame. - */ - public Rect getDisplayFrameLw(); - - /** - * Retrieve the frame of the content area that this window was last - * laid out in. This is the area in which the content of the window - * should be placed. It will be smaller than the display frame to - * account for screen decorations such as a status bar or soft - * keyboard. Must be called with the - * window manager lock held. - * - * @return Rect The rectangle holding the content frame. - */ - public Rect getContentFrameLw(); - - /** - * Retrieve the frame of the visible area that this window was last - * laid out in. This is the area of the screen in which the window - * will actually be fully visible. It will be smaller than the - * content frame to account for transient UI elements blocking it - * such as an input method's candidates UI. Must be called with the - * window manager lock held. - * - * @return Rect The rectangle holding the visible frame. - */ - public Rect getVisibleFrameLw(); - - /** - * Returns true if this window is waiting to receive its given - * internal insets from the client app, and so should not impact the - * layout of other windows. - */ - public boolean getGivenInsetsPendingLw(); - - /** - * Retrieve the insets given by this window's client for the content - * area of windows behind it. Must be called with the - * window manager lock held. - * - * @return Rect The left, top, right, and bottom insets, relative - * to the window's frame, of the actual contents. - */ - public Rect getGivenContentInsetsLw(); - - /** - * Retrieve the insets given by this window's client for the visible - * area of windows behind it. Must be called with the - * window manager lock held. - * - * @return Rect The left, top, right, and bottom insets, relative - * to the window's frame, of the actual visible area. - */ - public Rect getGivenVisibleInsetsLw(); - - /** * Retrieve the current LayoutParams of the window. * * @return WindowManager.LayoutParams The window's internal LayoutParams @@ -275,17 +192,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public WindowManager.LayoutParams getAttrs(); /** - * Retrieve the current system UI visibility flags associated with - * this window. - */ - public int getSystemUiVisibility(); - - /** - * Get the layer at which this window's surface will be Z-ordered. - */ - public int getSurfaceLayer(); - - /** * Retrieve the type of the top-level window. * * @return the base type of the parent window if attached or its own type otherwise @@ -301,22 +207,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public IApplicationToken getAppToken(); /** - * Return true if this window is participating in voice interaction. - */ - public boolean isVoiceInteraction(); - - /** - * Return true if, at any point, the application token associated with - * this window has actually displayed any windows. This is most useful - * with the "starting up" window to determine if any windows were - * displayed when it is closed. - * - * @return Returns true if one or more windows have been displayed, - * else false. - */ - public boolean hasAppShownWindows(); - - /** * Is this window visible? It is not visible if there is no * surface, or we are in the process of running an exit animation * that will remove the surface. @@ -324,42 +214,12 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { boolean isVisibleLw(); /** - * Is this window currently visible to the user on-screen? It is - * displayed either if it is visible or it is currently running an - * animation before no longer being visible. Must be called with the - * window manager lock held. - */ - boolean isDisplayedLw(); - - /** * Return true if this window (or a window it is attached to, but not * considering its app token) is currently animating. */ boolean isAnimatingLw(); /** - * Is this window considered to be gone for purposes of layout? - */ - boolean isGoneForLayoutLw(); - - /** - * Returns true if the window has a surface that it has drawn a - * complete UI in to. Note that this is different from {@link #hasDrawnLw()} - * in that it also returns true if the window is READY_TO_SHOW, but was not yet - * promoted to HAS_DRAWN. - */ - boolean isDrawnLw(); - - /** - * Returns true if this window has been shown on screen at some time in - * the past. Must be called with the window manager lock held. - * - * @deprecated Use {@link #isDrawnLw} or any of the other drawn/visibility methods. - */ - @Deprecated - public boolean hasDrawnLw(); - - /** * Can be called by the policy to force a window to be hidden, * regardless of whether the client or window manager would like * it shown. Must be called with the window manager lock held. @@ -377,51 +237,12 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public boolean showLw(boolean doAnimation); /** - * Check whether the process hosting this window is currently alive. - */ - public boolean isAlive(); - - /** - * Check if window is on {@link Display#DEFAULT_DISPLAY}. - * @return true if window is on default display. - */ - public boolean isDefaultDisplay(); - - /** * Check whether the window is currently dimming. */ public boolean isDimming(); - /** - * Returns true if the window is letterboxed for the display cutout. - */ - default boolean isLetterboxedForDisplayCutoutLw() { - return false; - } - - /** @return the current windowing mode of this window. */ - int getWindowingMode(); - - /** - * Returns the {@link WindowConfiguration.ActivityType} associated with the configuration - * of this window. - */ - default int getActivityType() { - return WindowConfiguration.WINDOWING_MODE_UNDEFINED; - } - - /** - * Returns true if the window is current in multi-windowing mode. i.e. it shares the - * screen with other application windows. - */ - boolean inMultiWindowMode(); - - public int getRotationAnimationHint(); - public boolean isInputMethodWindow(); - public boolean isInputMethodTarget(); - public int getDisplayId(); /** @@ -432,42 +253,8 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { return false; } - /** - * Returns true if the window owner has the permission to acquire a sleep token when it's - * visible. That is, they have the permission {@link Manifest.permission#DEVICE_POWER}. - */ - boolean canAcquireSleepToken(); - - /** @return true if this window desires key events. */ - boolean canReceiveKeys(); - /** @return true if the window can show over keyguard. */ boolean canShowWhenLocked(); - - /** - * Writes {@link com.android.server.wm.IdentifierProto} to stream. - */ - void writeIdentifierToProto(ProtoOutputStream proto, long fieldId); - - /** - * @return The {@link WindowFrames} associated with this {@link WindowState} - */ - WindowFrames getWindowFrames(); - } - - /** - * Representation of a input consumer that the policy has added to the - * window manager to consume input events going to windows below it. - */ - public interface InputConsumer { - /** - * Remove the input consumer from the window manager. - */ - void dismiss(); - /** - * Dispose the input consumer and input receiver from UI thread. - */ - void dispose(); } /** @@ -538,11 +325,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { void unregisterPointerEventListener(PointerEventListener listener, int displayId); /** - * @return The currently active input method window. - */ - WindowState getInputMethodWindowLw(); - - /** * Notifies window manager that {@link #isKeyguardTrustedLw} has changed. */ void notifyKeyguardTrustedChanged(); @@ -615,17 +397,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { } /** - * Provides the rotation of a device. - * - * @see com.android.server.policy.WindowOrientationListener - */ - public interface RotationSource { - int getProposedRotation(); - - void setCurrentRotation(int rotation); - } - - /** * Interface to get public information of a display content. */ public interface DisplayContentInfo { @@ -889,12 +660,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { } /** - * Get the highest layer (actually one more than) that the wallpaper is - * allowed to be in. - */ - public int getMaxWallpaperLayer(); - - /** * Return whether the given window can become the Keyguard window. Typically returns true for * the StatusBar. */ @@ -1384,17 +1149,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { void dumpDebug(ProtoOutputStream proto, long fieldId); /** - * Returns whether a given window type is considered a top level one. - * A top level window does not have a container, i.e. attached window, - * or if it has a container it is laid out as a top-level window, not - * as a child of its container. - * - * @param windowType The window type. - * @return True if the window is a top level one. - */ - public boolean isTopLevelWindow(int windowType); - - /** * Notifies the keyguard to start fading out. * * @param startTime the start time of the animation in uptime milliseconds diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java index 2b793c894eb0..f204aa2cc1ca 100644 --- a/services/core/java/com/android/server/security/VerityUtils.java +++ b/services/core/java/com/android/server/security/VerityUtils.java @@ -52,6 +52,9 @@ abstract public class VerityUtils { /** The maximum size of signature file. This is just to avoid potential abuse. */ private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192; + /** SHA256 hash size. */ + private static final int HASH_SIZE_BYTES = 32; + private static final boolean DEBUG = false; /** Returns true if the given file looks like containing an fs-verity signature. */ @@ -90,8 +93,23 @@ abstract public class VerityUtils { return (retval == 1); } + /** Returns hash of a root node for the fs-verity enabled file. */ + public static byte[] getFsverityRootHash(@NonNull String filePath) { + byte[] result = new byte[HASH_SIZE_BYTES]; + int retval = measureFsverityNative(filePath, result); + if (retval < 0) { + if (retval != -OsConstants.ENODATA) { + Slog.e(TAG, "Failed to measure fs-verity, errno " + -retval + ": " + filePath); + } + return null; + } + return result; + } + private static native int enableFsverityNative(@NonNull String filePath, @NonNull byte[] pkcs7Signature); + private static native int measureFsverityNative(@NonNull String filePath, + @NonNull byte[] digest); private static native int statxForFsverityNative(@NonNull String filePath); /** diff --git a/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java b/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java new file mode 100644 index 000000000000..1500cfaeb3a2 --- /dev/null +++ b/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java @@ -0,0 +1,62 @@ +/* + * 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 com.android.server.timezonedetector; + +import android.annotation.UserIdInt; +import android.os.Binder; +import android.os.UserHandle; + +/** + * An interface to wrap various difficult-to-intercept calls that services make to access / manage + * caller identity, e.g. {@link Binder#clearCallingIdentity()}. + */ +public interface CallerIdentityInjector { + + /** A singleton for the real implementation of {@link CallerIdentityInjector}. */ + CallerIdentityInjector REAL = new Real(); + + /** A {@link UserHandle#getCallingUserId()} call. */ + @UserIdInt int getCallingUserId(); + + /** A {@link Binder#clearCallingIdentity()} call. */ + long clearCallingIdentity(); + + /** A {@link Binder#restoreCallingIdentity(long)} ()} call. */ + void restoreCallingIdentity(long token); + + /** The real implementation of {@link CallerIdentityInjector}. */ + class Real implements CallerIdentityInjector { + + protected Real() { + } + + @Override + public int getCallingUserId() { + return UserHandle.getCallingUserId(); + } + + @Override + public long clearCallingIdentity() { + return Binder.clearCallingIdentity(); + } + + @Override + public void restoreCallingIdentity(long token) { + Binder.restoreCallingIdentity(token); + } + } +} diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java new file mode 100644 index 000000000000..4c7b1f38dd5a --- /dev/null +++ b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java @@ -0,0 +1,26 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.timezonedetector; + +/** + * A listener used to receive notification that time zone configuration has changed. + */ +@FunctionalInterface +public interface ConfigurationChangeListener { + /** Called when the current user or a configuration value has changed. */ + void onChange(); +} diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java new file mode 100644 index 000000000000..aee3d8d3499b --- /dev/null +++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java @@ -0,0 +1,288 @@ +/* + * 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.timezonedetector; + +import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.app.timezonedetector.TimeZoneCapabilities; +import android.app.timezonedetector.TimeZoneConfiguration; + +import java.util.Objects; + +/** + * Holds all configuration values that affect time zone behavior and some associated logic, e.g. + * {@link #getAutoDetectionEnabledBehavior()}, {@link #getGeoDetectionEnabledBehavior()} and {@link + * #createCapabilities()}. + */ +public final class ConfigurationInternal { + + private final @UserIdInt int mUserId; + private final boolean mUserConfigAllowed; + private final boolean mAutoDetectionSupported; + private final boolean mAutoDetectionEnabled; + private final boolean mLocationEnabled; + private final boolean mGeoDetectionEnabled; + + private ConfigurationInternal(Builder builder) { + mUserId = builder.mUserId; + mUserConfigAllowed = builder.mUserConfigAllowed; + mAutoDetectionSupported = builder.mAutoDetectionSupported; + mAutoDetectionEnabled = builder.mAutoDetectionEnabled; + mLocationEnabled = builder.mLocationEnabled; + mGeoDetectionEnabled = builder.mGeoDetectionEnabled; + } + + /** Returns the ID of the user this configuration is associated with. */ + public @UserIdInt int getUserId() { + return mUserId; + } + + /** Returns true if the user allowed to modify time zone configuration. */ + public boolean isUserConfigAllowed() { + return mUserConfigAllowed; + } + + /** Returns true if the device supports some form of auto time zone detection. */ + public boolean isAutoDetectionSupported() { + return mAutoDetectionSupported; + } + + /** Returns the value of the auto time zone detection enabled setting. */ + public boolean getAutoDetectionEnabledSetting() { + return mAutoDetectionEnabled; + } + + /** + * Returns true if auto time zone detection behavior is actually enabled, which can be distinct + * from the raw setting value. */ + public boolean getAutoDetectionEnabledBehavior() { + return mAutoDetectionSupported && mAutoDetectionEnabled; + } + + /** Returns true if user's location can be used generally. */ + public boolean isLocationEnabled() { + return mLocationEnabled; + } + + /** Returns the value of the geolocation time zone detection enabled setting. */ + public boolean getGeoDetectionEnabledSetting() { + return mGeoDetectionEnabled; + } + + /** + * Returns true if geolocation time zone detection behavior is actually enabled, which can be + * distinct from the raw setting value. + */ + public boolean getGeoDetectionEnabledBehavior() { + if (getAutoDetectionEnabledBehavior()) { + return mLocationEnabled && mGeoDetectionEnabled; + } + return false; + } + + /** Creates a {@link TimeZoneCapabilities} object using the configuration values. */ + public TimeZoneCapabilities createCapabilities() { + TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder() + .setConfiguration(asConfiguration()); + + boolean allowConfigDateTime = isUserConfigAllowed(); + + // Automatic time zone detection is only supported on devices if there is a telephony + // network available or geolocation time zone detection is possible. + boolean deviceHasTimeZoneDetection = isAutoDetectionSupported(); + + final int configureAutoDetectionEnabledCapability; + if (!deviceHasTimeZoneDetection) { + configureAutoDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED; + } else if (!allowConfigDateTime) { + configureAutoDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED; + } else { + configureAutoDetectionEnabledCapability = CAPABILITY_POSSESSED; + } + builder.setConfigureAutoDetectionEnabled(configureAutoDetectionEnabledCapability); + + final int configureGeolocationDetectionEnabledCapability; + if (!deviceHasTimeZoneDetection) { + configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED; + } else if (!allowConfigDateTime) { + configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED; + } else if (!isLocationEnabled()) { + configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_APPLICABLE; + } else { + configureGeolocationDetectionEnabledCapability = CAPABILITY_POSSESSED; + } + builder.setConfigureGeoDetectionEnabled(configureGeolocationDetectionEnabledCapability); + + // The ability to make manual time zone suggestions can also be restricted by policy. With + // the current logic above, this could lead to a situation where a device hardware does not + // support auto detection, the device has been forced into "auto" mode by an admin and the + // user is unable to disable auto detection. + final int suggestManualTimeZoneCapability; + if (!allowConfigDateTime) { + suggestManualTimeZoneCapability = CAPABILITY_NOT_ALLOWED; + } else if (getAutoDetectionEnabledBehavior()) { + suggestManualTimeZoneCapability = CAPABILITY_NOT_APPLICABLE; + } else { + suggestManualTimeZoneCapability = CAPABILITY_POSSESSED; + } + builder.setSuggestManualTimeZone(suggestManualTimeZoneCapability); + + return builder.build(); + } + + /** Returns a {@link TimeZoneConfiguration} from the configuration values. */ + public TimeZoneConfiguration asConfiguration() { + return new TimeZoneConfiguration.Builder(mUserId) + .setAutoDetectionEnabled(getAutoDetectionEnabledSetting()) + .setGeoDetectionEnabled(getGeoDetectionEnabledSetting()) + .build(); + } + + /** + * Merges the configuration values from this with any properties set in {@code + * newConfiguration}. The new configuration has precedence. Used to apply user updates to + * internal configuration. + */ + public ConfigurationInternal merge(TimeZoneConfiguration newConfiguration) { + Builder builder = new Builder(this); + if (newConfiguration.hasSetting(TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED)) { + builder.setAutoDetectionEnabled(newConfiguration.isAutoDetectionEnabled()); + } + if (newConfiguration.hasSetting(TimeZoneConfiguration.SETTING_GEO_DETECTION_ENABLED)) { + builder.setGeoDetectionEnabled(newConfiguration.isGeoDetectionEnabled()); + } + return builder.build(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ConfigurationInternal that = (ConfigurationInternal) o; + return mUserId == that.mUserId + && mUserConfigAllowed == that.mUserConfigAllowed + && mAutoDetectionSupported == that.mAutoDetectionSupported + && mAutoDetectionEnabled == that.mAutoDetectionEnabled + && mLocationEnabled == that.mLocationEnabled + && mGeoDetectionEnabled == that.mGeoDetectionEnabled; + } + + @Override + public int hashCode() { + return Objects.hash(mUserId, mUserConfigAllowed, mAutoDetectionSupported, + mAutoDetectionEnabled, mLocationEnabled, mGeoDetectionEnabled); + } + + @Override + public String toString() { + return "TimeZoneDetectorConfiguration{" + + "mUserId=" + mUserId + + "mUserConfigAllowed=" + mUserConfigAllowed + + "mAutoDetectionSupported=" + mAutoDetectionSupported + + "mAutoDetectionEnabled=" + mAutoDetectionEnabled + + "mLocationEnabled=" + mLocationEnabled + + "mGeoDetectionEnabled=" + mGeoDetectionEnabled + + '}'; + } + + /** + * A Builder for {@link ConfigurationInternal}. + */ + public static class Builder { + + private final @UserIdInt int mUserId; + private boolean mUserConfigAllowed; + private boolean mAutoDetectionSupported; + private boolean mAutoDetectionEnabled; + private boolean mLocationEnabled; + private boolean mGeoDetectionEnabled; + + /** + * Creates a new Builder with only the userId set. + */ + public Builder(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Creates a new Builder by copying values from an existing instance. + */ + public Builder(ConfigurationInternal toCopy) { + this.mUserId = toCopy.mUserId; + this.mUserConfigAllowed = toCopy.mUserConfigAllowed; + this.mAutoDetectionSupported = toCopy.mAutoDetectionSupported; + this.mAutoDetectionEnabled = toCopy.mAutoDetectionEnabled; + this.mLocationEnabled = toCopy.mLocationEnabled; + this.mGeoDetectionEnabled = toCopy.mGeoDetectionEnabled; + } + + /** + * Sets whether the user is allowed to configure time zone settings on this device. + */ + public Builder setUserConfigAllowed(boolean configAllowed) { + mUserConfigAllowed = configAllowed; + return this; + } + + /** + * Sets whether automatic time zone detection is supported on this device. + */ + public Builder setAutoDetectionSupported(boolean supported) { + mAutoDetectionSupported = supported; + return this; + } + + /** + * Sets the value of the automatic time zone detection enabled setting for this device. + */ + public Builder setAutoDetectionEnabled(boolean enabled) { + mAutoDetectionEnabled = enabled; + return this; + } + + /** + * Sets the value of the location mode setting for this user. + */ + public Builder setLocationEnabled(boolean enabled) { + mLocationEnabled = enabled; + return this; + } + + /** + * Sets the value of the geolocation time zone detection setting for this user. + */ + public Builder setGeoDetectionEnabled(boolean enabled) { + mGeoDetectionEnabled = enabled; + return this; + } + + /** Returns a new {@link ConfigurationInternal}. */ + @NonNull + public ConfigurationInternal build() { + return new ConfigurationInternal(this); + } + } +} diff --git a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING new file mode 100644 index 000000000000..91e172c9d153 --- /dev/null +++ b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.timezonedetector." + } + ] + } + ] +} diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java index 0ca36e0fc258..d64032325539 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java @@ -16,24 +16,30 @@ package com.android.server.timezonedetector; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED; +import static android.content.Intent.ACTION_USER_SWITCHED; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.ActivityManagerInternal; import android.app.AlarmManager; -import android.app.timezonedetector.TimeZoneCapabilities; import android.app.timezonedetector.TimeZoneConfiguration; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.location.LocationManager; import android.net.ConnectivityManager; +import android.os.Handler; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.util.Slog; + +import com.android.server.LocalServices; import java.util.Objects; @@ -42,103 +48,87 @@ import java.util.Objects; */ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrategyImpl.Callback { + private static final String LOG_TAG = "TimeZoneDetectorCallbackImpl"; private static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; - private final Context mContext; - private final ContentResolver mCr; - private final UserManager mUserManager; - - TimeZoneDetectorCallbackImpl(Context context) { - mContext = context; + @NonNull private final Context mContext; + @NonNull private final Handler mHandler; + @NonNull private final ContentResolver mCr; + @NonNull private final UserManager mUserManager; + @NonNull private final boolean mGeoDetectionFeatureEnabled; + @NonNull private final LocationManager mLocationManager; + // @NonNull after setConfigChangeListener() is called. + private ConfigurationChangeListener mConfigChangeListener; + + TimeZoneDetectorCallbackImpl(@NonNull Context context, @NonNull Handler handler, + boolean geoDetectionFeatureEnabled) { + mContext = Objects.requireNonNull(context); + mHandler = Objects.requireNonNull(handler); mCr = context.getContentResolver(); mUserManager = context.getSystemService(UserManager.class); + mLocationManager = context.getSystemService(LocationManager.class); + mGeoDetectionFeatureEnabled = geoDetectionFeatureEnabled; + + // Wire up the change listener. All invocations are performed on the mHandler thread. + + // Listen for the user changing / the user's location mode changing. + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_USER_SWITCHED); + filter.addAction(LocationManager.MODE_CHANGED_ACTION); + mContext.registerReceiverForAllUsers(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + handleConfigChangeOnHandlerThread(); + } + }, filter, null, mHandler); + + // Add async callbacks for global settings being changed. + ContentResolver contentResolver = mContext.getContentResolver(); + contentResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true, + new ContentObserver(mHandler) { + public void onChange(boolean selfChange) { + handleConfigChangeOnHandlerThread(); + } + }); + + // Add async callbacks for user scoped location settings being changed. + contentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED), + true, + new ContentObserver(mHandler) { + public void onChange(boolean selfChange) { + handleConfigChangeOnHandlerThread(); + } + }, UserHandle.USER_ALL); } - @Override - public TimeZoneCapabilities getCapabilities(@UserIdInt int userId) { - UserHandle userHandle = UserHandle.of(userId); - boolean disallowConfigDateTime = - mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle); - - TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(userId); - - // Automatic time zone detection is only supported (currently) on devices if there is a - // telephony network available. - if (!deviceHasTelephonyNetwork()) { - builder.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_SUPPORTED); - } else if (disallowConfigDateTime) { - builder.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED); - } else { - builder.setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED); - } - - // TODO(b/149014708) Replace this with real logic when the settings storage is fully - // implemented. - builder.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_SUPPORTED); - - // The ability to make manual time zone suggestions can also be restricted by policy. With - // the current logic above, this could lead to a situation where a device hardware does not - // support auto detection, the device has been forced into "auto" mode by an admin and the - // user is unable to disable auto detection. - if (disallowConfigDateTime) { - builder.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED); - } else if (isAutoDetectionEnabled()) { - builder.setSuggestManualTimeZone(CAPABILITY_NOT_APPLICABLE); - } else { - builder.setSuggestManualTimeZone(CAPABILITY_POSSESSED); + private void handleConfigChangeOnHandlerThread() { + if (mConfigChangeListener == null) { + Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null"); } - return builder.build(); + mConfigChangeListener.onChange(); } @Override - public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) { - return new TimeZoneConfiguration.Builder() - .setAutoDetectionEnabled(isAutoDetectionEnabled()) - .setGeoDetectionEnabled(isGeoDetectionEnabled()) - .build(); + public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) { + mConfigChangeListener = Objects.requireNonNull(listener); } @Override - public void setConfiguration( - @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration) { - Objects.requireNonNull(configuration); - if (!configuration.isComplete()) { - throw new IllegalArgumentException("configuration=" + configuration + " not complete"); - } - - // Avoid writing auto detection config for devices that do not support auto time zone - // detection: if we wrote it down then we'd set the default explicitly. That might influence - // what happens on later releases that do support auto detection on the same hardware. - if (isAutoDetectionSupported()) { - final int autoEnabledValue = configuration.isAutoDetectionEnabled() ? 1 : 0; - Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, autoEnabledValue); - - final boolean geoTzDetectionEnabledValue = configuration.isGeoDetectionEnabled(); - // TODO(b/149014708) Write this down to user-scoped settings once implemented. - } - } - - @Override - public boolean isAutoDetectionEnabled() { - // To ensure that TimeZoneConfiguration is "complete" for simplicity, devices that do not - // support auto detection have safe, hard coded configuration values that make it look like - // auto detection is turned off. It is therefore important that false is returned from this - // method for devices that do not support auto time zone detection. Such devices will not - // have a UI to turn the auto detection on/off. Returning true could prevent the user - // entering information manually. On devices that do support auto time detection the default - // is to turn auto detection on. - if (isAutoDetectionSupported()) { - return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0; - } - return false; + public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) { + return new ConfigurationInternal.Builder(userId) + .setUserConfigAllowed(isUserConfigAllowed(userId)) + .setAutoDetectionSupported(isAutoDetectionSupported()) + .setAutoDetectionEnabled(isAutoDetectionEnabled()) + .setLocationEnabled(isLocationEnabled(userId)) + .setGeoDetectionEnabled(isGeoDetectionEnabled(userId)) + .build(); } @Override - public boolean isGeoDetectionEnabled() { - // TODO(b/149014708) Read this from user-scoped settings once implemented. The user's - // location toggle will act as an override for this setting, i.e. so that the setting will - // return false if the location toggle is disabled. - return false; + public @UserIdInt int getCurrentUserId() { + return LocalServices.getService(ActivityManagerInternal.class).getCurrentUserId(); } @Override @@ -165,8 +155,55 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat alarmManager.setTimeZone(zoneId); } + @Override + public void storeConfiguration(TimeZoneConfiguration configuration) { + Objects.requireNonNull(configuration); + + // Avoid writing the auto detection enabled setting for devices that do not support auto + // time zone detection: if we wrote it down then we'd set the value explicitly, which would + // prevent detecting "default" later. That might influence what happens on later releases + // that support new types of auto detection on the same hardware. + if (isAutoDetectionSupported()) { + final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled(); + setAutoDetectionEnabled(autoDetectionEnabled); + + final int userId = configuration.getUserId(); + final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled(); + setGeoDetectionEnabled(userId, geoTzDetectionEnabled); + } + } + + private boolean isUserConfigAllowed(@UserIdInt int userId) { + UserHandle userHandle = UserHandle.of(userId); + return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle); + } + private boolean isAutoDetectionSupported() { - return deviceHasTelephonyNetwork(); + return deviceHasTelephonyNetwork() || mGeoDetectionFeatureEnabled; + } + + private boolean isAutoDetectionEnabled() { + return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0; + } + + private void setAutoDetectionEnabled(boolean enabled) { + Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, enabled ? 1 : 0); + } + + private boolean isLocationEnabled(@UserIdInt int userId) { + return mLocationManager.isLocationEnabledForUser(UserHandle.of(userId)); + } + + private boolean isGeoDetectionEnabled(@UserIdInt int userId) { + final boolean locationEnabled = isLocationEnabled(userId); + return Settings.Secure.getIntForUser(mCr, + Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, + locationEnabled ? 1 : 0 /* defaultValue */, userId) != 0; + } + + private void setGeoDetectionEnabled(@UserIdInt int userId, boolean enabled) { + Settings.Secure.putIntForUser(mCr, Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, + enabled ? 1 : 0, userId); } private boolean deviceHasTelephonyNetwork() { @@ -174,4 +211,4 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat return mContext.getSystemService(ConnectivityManager.class) .isNetworkSupported(ConnectivityManager.TYPE_MOBILE); } -} +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java index fb7a73d12632..2d50390c27a9 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java @@ -27,6 +27,12 @@ import android.annotation.NonNull; */ public interface TimeZoneDetectorInternal extends Dumpable.Container { + /** Adds a listener that will be invoked when time zone detection configuration is changed. */ + void addConfigurationListener(ConfigurationChangeListener listener); + + /** Returns the {@link ConfigurationInternal} for the current user. */ + ConfigurationInternal getCurrentUserConfigurationInternal(); + /** * Suggests the current time zone, determined using geolocation, to the detector. The * detector may ignore the signal based on system settings, whether better information is diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java index 15412a0d14a1..f0ce827cec5e 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java @@ -20,8 +20,8 @@ import android.annotation.NonNull; import android.content.Context; import android.os.Handler; -import com.android.internal.annotations.VisibleForTesting; - +import java.util.ArrayList; +import java.util.List; import java.util.Objects; /** @@ -34,18 +34,26 @@ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInter @NonNull private final Context mContext; @NonNull private final Handler mHandler; @NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy; + @NonNull private final List<ConfigurationChangeListener> mConfigurationListeners = + new ArrayList<>(); - static TimeZoneDetectorInternalImpl create(@NonNull Context context, @NonNull Handler handler, - @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) { - return new TimeZoneDetectorInternalImpl(context, handler, timeZoneDetectorStrategy); - } - - @VisibleForTesting public TimeZoneDetectorInternalImpl(@NonNull Context context, @NonNull Handler handler, @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) { mContext = Objects.requireNonNull(context); mHandler = Objects.requireNonNull(handler); mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy); + + // Wire up a change listener so that any downstream listeners can be notified when + // the configuration changes for any reason. + mTimeZoneDetectorStrategy.addConfigChangeListener(this::handleConfigurationChanged); + } + + private void handleConfigurationChanged() { + synchronized (mConfigurationListeners) { + for (ConfigurationChangeListener listener : mConfigurationListeners) { + listener.onChange(); + } + } } @Override @@ -54,6 +62,19 @@ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInter } @Override + public void addConfigurationListener(ConfigurationChangeListener listener) { + synchronized (mConfigurationListeners) { + mConfigurationListeners.add(Objects.requireNonNull(listener)); + } + } + + @Override + @NonNull + public ConfigurationInternal getCurrentUserConfigurationInternal() { + return mTimeZoneDetectorStrategy.getCurrentUserConfigurationInternal(); + } + + @Override public void suggestGeolocationTimeZone( @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) { Objects.requireNonNull(timeZoneSuggestion); diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index d81f949742bd..7501d9fe6a7c 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -24,32 +24,24 @@ import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.app.timezonedetector.TimeZoneCapabilities; import android.app.timezonedetector.TimeZoneConfiguration; -import android.content.ContentResolver; import android.content.Context; -import android.database.ContentObserver; -import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; -import android.os.UserHandle; -import android.provider.Settings; import android.util.IndentingPrintWriter; import android.util.Slog; -import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.server.FgThread; import com.android.server.SystemService; -import com.android.server.timezonedetector.TimeZoneDetectorStrategy.StrategyListener; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Iterator; import java.util.Objects; /** @@ -65,6 +57,9 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub private static final String TAG = "TimeZoneDetectorService"; + /** A compile time switch for enabling / disabling geolocation-based time zone detection. */ + private static final boolean GEOLOCATION_TIME_ZONE_DETECTION_ENABLED = false; + /** * Handles the service lifecycle for {@link TimeZoneDetectorService} and * {@link TimeZoneDetectorInternalImpl}. @@ -80,18 +75,20 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub // Obtain / create the shared dependencies. Context context = getContext(); Handler handler = FgThread.getHandler(); + TimeZoneDetectorStrategy timeZoneDetectorStrategy = - TimeZoneDetectorStrategyImpl.create(context); + TimeZoneDetectorStrategyImpl.create( + context, handler, GEOLOCATION_TIME_ZONE_DETECTION_ENABLED); // Create and publish the local service for use by internal callers. TimeZoneDetectorInternal internal = - TimeZoneDetectorInternalImpl.create(context, handler, timeZoneDetectorStrategy); + new TimeZoneDetectorInternalImpl(context, handler, timeZoneDetectorStrategy); publishLocalService(TimeZoneDetectorInternal.class, internal); // Publish the binder service so it can be accessed from other (appropriately // permissioned) processes. - TimeZoneDetectorService service = - TimeZoneDetectorService.create(context, handler, timeZoneDetectorStrategy); + TimeZoneDetectorService service = TimeZoneDetectorService.create( + context, handler, timeZoneDetectorStrategy); publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service); } } @@ -103,79 +100,51 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub private final Handler mHandler; @NonNull + private final CallerIdentityInjector mCallerIdentityInjector; + + @NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy; - /** - * This sparse array acts as a map from userId to listeners running as that userId. User scoped - * as time zone detection configuration is partially user-specific, so different users can - * get different configuration. - */ @GuardedBy("mConfigurationListeners") @NonNull - private final SparseArray<ArrayList<ITimeZoneConfigurationListener>> mConfigurationListeners = - new SparseArray<>(); + private final ArrayList<ITimeZoneConfigurationListener> mConfigurationListeners = + new ArrayList<>(); private static TimeZoneDetectorService create( @NonNull Context context, @NonNull Handler handler, @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) { - TimeZoneDetectorService service = - new TimeZoneDetectorService(context, handler, timeZoneDetectorStrategy); - - ContentResolver contentResolver = context.getContentResolver(); - contentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true, - new ContentObserver(handler) { - public void onChange(boolean selfChange) { - service.handleAutoTimeZoneConfigChanged(); - } - }); - // TODO(b/149014708) Listen for changes to geolocation time zone detection enabled config. - // This should also include listening to the current user and the current user's location - // toggle since the config is user-scoped and the location toggle overrides the geolocation - // time zone enabled setting. + CallerIdentityInjector callerIdentityInjector = CallerIdentityInjector.REAL; + TimeZoneDetectorService service = new TimeZoneDetectorService( + context, handler, callerIdentityInjector, timeZoneDetectorStrategy); return service; } @VisibleForTesting public TimeZoneDetectorService(@NonNull Context context, @NonNull Handler handler, + @NonNull CallerIdentityInjector callerIdentityInjector, @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) { mContext = Objects.requireNonNull(context); mHandler = Objects.requireNonNull(handler); + mCallerIdentityInjector = Objects.requireNonNull(callerIdentityInjector); mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy); - mTimeZoneDetectorStrategy.setStrategyListener(new StrategyListener() { - @Override - public void onConfigurationChanged() { - handleConfigurationChanged(); - } - }); - } - - @Override - @NonNull - public TimeZoneCapabilities getCapabilities() { - enforceManageTimeZoneDetectorConfigurationPermission(); - int userId = UserHandle.getCallingUserId(); - long token = Binder.clearCallingIdentity(); - try { - return mTimeZoneDetectorStrategy.getCapabilities(userId); - } finally { - Binder.restoreCallingIdentity(token); - } + // Wire up a change listener so that ITimeZoneConfigurationListeners can be notified when + // the configuration changes for any reason. + mTimeZoneDetectorStrategy.addConfigChangeListener(this::handleConfigurationChanged); } @Override @NonNull - public TimeZoneConfiguration getConfiguration() { + public TimeZoneCapabilities getCapabilities() { enforceManageTimeZoneDetectorConfigurationPermission(); - int userId = UserHandle.getCallingUserId(); - long token = Binder.clearCallingIdentity(); + int userId = mCallerIdentityInjector.getCallingUserId(); + long token = mCallerIdentityInjector.clearCallingIdentity(); try { - return mTimeZoneDetectorStrategy.getConfiguration(userId); + return mTimeZoneDetectorStrategy.getConfigurationInternal(userId).createCapabilities(); } finally { - Binder.restoreCallingIdentity(token); + mCallerIdentityInjector.restoreCallingIdentity(token); } } @@ -184,12 +153,16 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub enforceManageTimeZoneDetectorConfigurationPermission(); Objects.requireNonNull(configuration); - int userId = UserHandle.getCallingUserId(); - long token = Binder.clearCallingIdentity(); + int callingUserId = mCallerIdentityInjector.getCallingUserId(); + if (callingUserId != configuration.getUserId()) { + return false; + } + + long token = mCallerIdentityInjector.clearCallingIdentity(); try { - return mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration); + return mTimeZoneDetectorStrategy.updateConfiguration(configuration); } finally { - Binder.restoreCallingIdentity(token); + mCallerIdentityInjector.restoreCallingIdentity(token); } } @@ -197,25 +170,17 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub public void addConfigurationListener(@NonNull ITimeZoneConfigurationListener listener) { enforceManageTimeZoneDetectorConfigurationPermission(); Objects.requireNonNull(listener); - int userId = UserHandle.getCallingUserId(); synchronized (mConfigurationListeners) { - ArrayList<ITimeZoneConfigurationListener> listeners = - mConfigurationListeners.get(userId); - if (listeners != null && listeners.contains(listener)) { + if (mConfigurationListeners.contains(listener)) { return; } try { - if (listeners == null) { - listeners = new ArrayList<>(1); - mConfigurationListeners.put(userId, listeners); - } - // Ensure the reference to the listener will be removed if the client process dies. listener.asBinder().linkToDeath(this, 0 /* flags */); // Only add the listener if we can linkToDeath(). - listeners.add(listener); + mConfigurationListeners.add(listener); } catch (RemoteException e) { Slog.e(TAG, "Unable to linkToDeath() for listener=" + listener, e); } @@ -226,19 +191,16 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub public void removeConfigurationListener(@NonNull ITimeZoneConfigurationListener listener) { enforceManageTimeZoneDetectorConfigurationPermission(); Objects.requireNonNull(listener); - int userId = UserHandle.getCallingUserId(); synchronized (mConfigurationListeners) { boolean removedListener = false; - ArrayList<ITimeZoneConfigurationListener> userListeners = - mConfigurationListeners.get(userId); - if (userListeners.remove(listener)) { + if (mConfigurationListeners.remove(listener)) { // Stop listening for the client process to die. listener.asBinder().unlinkToDeath(this, 0 /* flags */); removedListener = true; } if (!removedListener) { - Slog.w(TAG, "Client asked to remove listenener=" + listener + Slog.w(TAG, "Client asked to remove listener=" + listener + ", but no listeners were removed." + " mConfigurationListeners=" + mConfigurationListeners); } @@ -259,19 +221,14 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub public void binderDied(IBinder who) { synchronized (mConfigurationListeners) { boolean removedListener = false; - final int userCount = mConfigurationListeners.size(); - for (int i = 0; i < userCount; i++) { - ArrayList<ITimeZoneConfigurationListener> userListeners = - mConfigurationListeners.valueAt(i); - Iterator<ITimeZoneConfigurationListener> userListenerIterator = - userListeners.iterator(); - while (userListenerIterator.hasNext()) { - ITimeZoneConfigurationListener userListener = userListenerIterator.next(); - if (userListener.asBinder().equals(who)) { - userListenerIterator.remove(); - removedListener = true; - break; - } + final int listenerCount = mConfigurationListeners.size(); + for (int listenerIndex = listenerCount - 1; listenerIndex >= 0; listenerIndex--) { + ITimeZoneConfigurationListener listener = + mConfigurationListeners.get(listenerIndex); + if (listener.asBinder().equals(who)) { + mConfigurationListeners.remove(listenerIndex); + removedListener = true; + break; } } if (!removedListener) { @@ -283,42 +240,25 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub } void handleConfigurationChanged() { - // Note: we could trigger an async time zone detection operation here via a call to - // handleAutoTimeZoneConfigChanged(), but that is triggered in response to the underlying - // setting value changing so it is currently unnecessary. If we get to a point where all - // configuration changes are guaranteed to happen in response to an updateConfiguration() - // call, then we can remove that path and call it here instead. - // Configuration has changed, but each user may have a different view of the configuration. // It's possible that this will cause unnecessary notifications but that shouldn't be a // problem. synchronized (mConfigurationListeners) { - final int userCount = mConfigurationListeners.size(); - for (int userIndex = 0; userIndex < userCount; userIndex++) { - int userId = mConfigurationListeners.keyAt(userIndex); - TimeZoneConfiguration configuration = - mTimeZoneDetectorStrategy.getConfiguration(userId); - - ArrayList<ITimeZoneConfigurationListener> listeners = - mConfigurationListeners.valueAt(userIndex); - final int listenerCount = listeners.size(); - for (int listenerIndex = 0; listenerIndex < listenerCount; listenerIndex++) { - ITimeZoneConfigurationListener listener = listeners.get(listenerIndex); - try { - listener.onChange(configuration); - } catch (RemoteException e) { - Slog.w(TAG, "Unable to notify listener=" + listener - + " for userId=" + userId - + " of updated configuration=" + configuration, e); - } + final int listenerCount = mConfigurationListeners.size(); + for (int listenerIndex = 0; listenerIndex < listenerCount; listenerIndex++) { + ITimeZoneConfigurationListener listener = + mConfigurationListeners.get(listenerIndex); + try { + listener.onChange(); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to notify listener=" + listener, e); } } } } /** Provided for command-line access. This is not exposed as a binder API. */ - void suggestGeolocationTimeZone( - @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) { + void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) { enforceSuggestGeolocationTimeZonePermission(); Objects.requireNonNull(timeZoneSuggestion); @@ -331,12 +271,12 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub enforceSuggestManualTimeZonePermission(); Objects.requireNonNull(timeZoneSuggestion); - int userId = UserHandle.getCallingUserId(); - long token = Binder.clearCallingIdentity(); + int userId = mCallerIdentityInjector.getCallingUserId(); + long token = mCallerIdentityInjector.clearCallingIdentity(); try { return mTimeZoneDetectorStrategy.suggestManualTimeZone(userId, timeZoneSuggestion); } finally { - Binder.restoreCallingIdentity(token); + mCallerIdentityInjector.restoreCallingIdentity(token); } } @@ -358,12 +298,6 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub ipw.flush(); } - /** Internal method for handling the auto time zone configuration being changed. */ - @VisibleForTesting - public void handleAutoTimeZoneConfigChanged() { - mHandler.post(mTimeZoneDetectorStrategy::handleAutoTimeZoneConfigChanged); - } - private void enforceManageTimeZoneDetectorConfigurationPermission() { // TODO Switch to a dedicated MANAGE_TIME_AND_ZONE_CONFIGURATION permission. mContext.enforceCallingPermission( diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java index c5b7e39f4fef..f944c5638fa9 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java @@ -19,51 +19,84 @@ import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; -import android.app.timezonedetector.TimeZoneCapabilities; import android.app.timezonedetector.TimeZoneConfiguration; import android.util.IndentingPrintWriter; /** - * The interface for the class that implements the time detection algorithm used by the - * {@link TimeZoneDetectorService}. + * The interface for the class that is responsible for setting the time zone on a device, used by + * {@link TimeZoneDetectorService} and {@link TimeZoneDetectorInternal}. * - * <p>The strategy uses suggestions to decide whether to modify the device's time zone setting - * and what to set it to. + * <p>The strategy receives suggestions, which it may use to modify the device's time zone setting. + * Suggestions are acted on or ignored as needed, depending on previously received suggestions and + * the current user's configuration (see {@link ConfigurationInternal}). * - * <p>Most calls will be handled by a single thread, but that is not true for all calls. For example - * {@link #dump(IndentingPrintWriter, String[])}) may be called on a different thread concurrently - * with other operations so implementations must still handle thread safety. + * <p>Devices can have zero, one or two automatic time zone detection algorithm available at any + * point in time. + * + * <p>The two automatic detection algorithms supported are "telephony" and "geolocation". Algorithm + * availability and use depends on several factors: + * <ul> + * <li>Telephony is only available on devices with a telephony stack. + * <li>Geolocation is also optional and configured at image creation time. When enabled on a + * device, its availability depends on the current user's settings, so switching between users can + * change the automatic algorithm used by the device.</li> + * </ul> + * + * <p>If there are no automatic time zone detections algorithms available then the user can usually + * change the device time zone manually. Under most circumstances the current user can turn + * automatic time zone detection on or off, or choose the algorithm via settings. + * + * <p>Telephony detection is independent of the current user. The device keeps track of the most + * recent telephony suggestion from each slotIndex. When telephony detection is in use, the highest + * scoring suggestion is used to set the device time zone based on a scoring algorithm. If several + * slotIndexes provide the same score then the slotIndex with the lowest numeric value "wins". If + * the situation changes and it is no longer possible to be confident about the time zone, + * slotIndexes must have an empty suggestion submitted in order to "withdraw" their previous + * suggestion otherwise it will remain in use. + * + * <p>Geolocation detection is dependent on the current user and their settings. The device retains + * at most one geolocation suggestion. Generally, use of a device's location is dependent on the + * user's "location toggle", but even when that is enabled the user may choose to enable / disable + * the use of geolocation for device time zone detection. If the current user changes to one that + * does not have geolocation detection enabled, or the user turns off geolocation detection, then + * the strategy discards the latest geolocation suggestion. Devices that lose a location fix must + * have an empty suggestion submitted in order to "withdraw" their previous suggestion otherwise it + * will remain in use. + * + * <p>Threading: + * + * <p>Suggestion calls with a void return type may be handed off to a separate thread and handled + * asynchronously. Synchronous calls like {@link #getCurrentUserConfigurationInternal()}, and debug + * calls like {@link #dump(IndentingPrintWriter, String[])}, may be called on a different thread + * concurrently with other operations. * * @hide */ public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container { - /** A listener for strategy events. */ - interface StrategyListener { - /** - * Invoked when configuration has been changed. - */ - void onConfigurationChanged(); - } - - /** Sets the listener that enables the strategy to communicate with the surrounding service. */ - void setStrategyListener(@NonNull StrategyListener listener); + /** + * Sets a listener that will be triggered whenever time zone detection configuration is + * changed. + */ + void addConfigChangeListener(@NonNull ConfigurationChangeListener listener); - /** Returns the user's time zone capabilities. */ + /** Returns the user's time zone configuration. */ @NonNull - TimeZoneCapabilities getCapabilities(@UserIdInt int userId); + ConfigurationInternal getConfigurationInternal(@UserIdInt int userId); /** - * Returns the configuration that controls time zone detector behavior. + * Returns the configuration that controls time zone detector behavior for the current user. */ @NonNull - TimeZoneConfiguration getConfiguration(@UserIdInt int userId); + ConfigurationInternal getCurrentUserConfigurationInternal(); /** - * Updates the configuration settings that control time zone detector behavior. + * Updates the configuration properties that control a device's time zone behavior. + * + * <p>This method returns {@code true} if the configuration was changed, + * {@code false} otherwise. */ - boolean updateConfiguration( - @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration); + boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration); /** * Suggests zero, one or more time zones for the device, or withdraws a previous suggestion if @@ -85,9 +118,4 @@ public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container { * suggestion. */ void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion); - - /** - * Called when there has been a change to the automatic time zone detection configuration. - */ - void handleAutoTimeZoneConfigChanged(); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java index d1369a289428..8a42b18b514f 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -20,10 +20,7 @@ import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYP import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED; -import static android.app.timezonedetector.TimeZoneConfiguration.PROPERTY_AUTO_DETECTION_ENABLED; -import static android.app.timezonedetector.TimeZoneConfiguration.PROPERTY_GEO_DETECTION_ENABLED; import android.annotation.NonNull; import android.annotation.Nullable; @@ -33,6 +30,7 @@ import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.app.timezonedetector.TimeZoneCapabilities; import android.app.timezonedetector.TimeZoneConfiguration; import android.content.Context; +import android.os.Handler; import android.util.IndentingPrintWriter; import android.util.LocalLog; import android.util.Slog; @@ -45,15 +43,7 @@ import java.util.List; import java.util.Objects; /** - * An implementation of {@link TimeZoneDetectorStrategy} that handle telephony and manual - * suggestions. Suggestions are acted on or ignored as needed, dependent on the current "auto time - * zone detection" setting. - * - * <p>For automatic detection, it keeps track of the most recent telephony suggestion from each - * slotIndex and it uses the best suggestion based on a scoring algorithm. If several slotIndexes - * provide the same score then the slotIndex with the lowest numeric value "wins". If the situation - * changes and it is no longer possible to be confident about the time zone, slotIndexes must have - * an empty suggestion submitted in order to "withdraw" their previous suggestion. + * The real implementation of {@link TimeZoneDetectorStrategy}. * * <p>Most public methods are marked synchronized to ensure thread safety around internal state. */ @@ -61,48 +51,27 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat /** * Used by {@link TimeZoneDetectorStrategyImpl} to interact with device configuration / settings - * / system properties. It can be faked for testing different scenarios. + * / system properties. It can be faked for testing. * * <p>Note: Because the settings / system properties-derived values can currently be modified - * independently and from different threads (and processes!), their use are prone to race - * conditions. That will be true until the responsibility for setting their values is moved to - * {@link TimeZoneDetectorStrategyImpl} (which is thread safe). + * independently and from different threads (and processes!), their use is prone to race + * conditions. */ @VisibleForTesting public interface Callback { /** - * Returns the capabilities for the user. - */ - @NonNull - TimeZoneCapabilities getCapabilities(@UserIdInt int userId); - - /** - * Returns the configuration for the user. - * @param userId + * Sets a {@link ConfigurationChangeListener} that will be invoked when there are any + * changes that could affect time zone detection. This is invoked during system server + * setup. */ - @NonNull - TimeZoneConfiguration getConfiguration(int userId); + void setConfigChangeListener(@NonNull ConfigurationChangeListener listener); - /** - * Sets the configuration for the user. This method handles storage only, the configuration - * must have been validated by the caller and be complete. - * - * @throws IllegalArgumentException if {@link TimeZoneConfiguration#isComplete()} - * returns {@code false} - */ - void setConfiguration(@UserIdInt int userId, @NonNull TimeZoneConfiguration configuration); - - /** - * Returns true if automatic time zone detection is currently enabled. - */ - boolean isAutoDetectionEnabled(); + /** Returns the current user at the instant it is called. */ + @UserIdInt int getCurrentUserId(); - /** - * Returns whether geolocation can be used for time zone detection when {@link - * #isAutoDetectionEnabled()} returns {@code true}. - */ - boolean isGeoDetectionEnabled(); + /** Returns the {@link ConfigurationInternal} for the specified user. */ + ConfigurationInternal getConfigurationInternal(@UserIdInt int userId); /** * Returns true if the device has had an explicit time zone set. @@ -118,6 +87,13 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat * Sets the device's time zone. */ void setDeviceTimeZone(@NonNull String zoneId); + + /** + * Stores the configuration properties contained in {@code newConfiguration}. + * All checks about user capabilities must be done by the caller and + * {@link TimeZoneConfiguration#isComplete()} must be {@code true}. + */ + void storeConfiguration(TimeZoneConfiguration newConfiguration); } private static final String LOG_TAG = "TimeZoneDetectorStrategy"; @@ -189,9 +165,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat @NonNull private final Callback mCallback; - /** Non-null after {@link #setStrategyListener(StrategyListener)} is called. */ - @Nullable - private StrategyListener mListener; + @GuardedBy("this") + @NonNull + private List<ConfigurationChangeListener> mConfigChangeListeners = new ArrayList<>(); /** * A log that records the decisions / decision metadata that affected the device's time zone. @@ -211,7 +187,8 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE); /** - * The latest geolocation suggestion received. + * The latest geolocation suggestion received. If the user disabled geolocation time zone + * detection then the latest suggestion is cleared. */ @GuardedBy("this") private ReferenceWithHistory<GeolocationTimeZoneSuggestion> mLatestGeoLocationSuggestion = @@ -223,113 +200,120 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat /** * Creates a new instance of {@link TimeZoneDetectorStrategyImpl}. */ - public static TimeZoneDetectorStrategyImpl create(Context context) { - Callback timeZoneDetectionServiceHelper = new TimeZoneDetectorCallbackImpl(context); - return new TimeZoneDetectorStrategyImpl(timeZoneDetectionServiceHelper); + public static TimeZoneDetectorStrategyImpl create( + @NonNull Context context, @NonNull Handler handler, + boolean geolocationTimeZoneDetectionEnabled) { + + TimeZoneDetectorCallbackImpl callback = new TimeZoneDetectorCallbackImpl( + context, handler, geolocationTimeZoneDetectionEnabled); + return new TimeZoneDetectorStrategyImpl(callback); } @VisibleForTesting - public TimeZoneDetectorStrategyImpl(Callback callback) { + public TimeZoneDetectorStrategyImpl(@NonNull Callback callback) { mCallback = Objects.requireNonNull(callback); + mCallback.setConfigChangeListener(this::handleConfigChanged); } /** - * Sets a listener that allows the strategy to communicate with the surrounding service. This - * must be called before the instance is used and must only be called once. + * Adds a listener that allows the strategy to communicate with the surrounding service / + * internal. This must be called before the instance is used. */ @Override - public synchronized void setStrategyListener(@NonNull StrategyListener listener) { - if (mListener != null) { - throw new IllegalStateException("Strategy already has a listener"); - } - mListener = Objects.requireNonNull(listener); + public synchronized void addConfigChangeListener( + @NonNull ConfigurationChangeListener listener) { + Objects.requireNonNull(listener); + mConfigChangeListeners.add(listener); } @Override @NonNull - public synchronized TimeZoneCapabilities getCapabilities(@UserIdInt int userId) { - return mCallback.getCapabilities(userId); + public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) { + return mCallback.getConfigurationInternal(userId); } @Override @NonNull - public synchronized TimeZoneConfiguration getConfiguration(@UserIdInt int userId) { - return mCallback.getConfiguration(userId); + public synchronized ConfigurationInternal getCurrentUserConfigurationInternal() { + int currentUserId = mCallback.getCurrentUserId(); + return getConfigurationInternal(currentUserId); } @Override public synchronized boolean updateConfiguration( - @UserIdInt int userId, @NonNull TimeZoneConfiguration configurationChanges) { - Objects.requireNonNull(configurationChanges); - - // Validate the requested configuration changes before applying any of them. - TimeZoneCapabilities capabilities = mCallback.getCapabilities(userId); - boolean canManageTimeZoneDetection = - capabilities.getConfigureAutoDetectionEnabled() >= CAPABILITY_NOT_APPLICABLE; - if (!canManageTimeZoneDetection - && containsAutoTimeDetectionProperties(configurationChanges)) { + @NonNull TimeZoneConfiguration requestedConfiguration) { + Objects.requireNonNull(requestedConfiguration); + + int userId = requestedConfiguration.getUserId(); + TimeZoneCapabilities capabilities = getConfigurationInternal(userId).createCapabilities(); + + // Create a new configuration builder, and copy across the mutable properties users are + // able to modify. Other properties are therefore ignored. + final TimeZoneConfiguration newConfiguration = + capabilities.applyUpdate(requestedConfiguration); + if (newConfiguration == null) { + // The changes could not be made due to return false; } - // Create a complete configuration by merging the existing and new (possibly partial) - // configuration. - final TimeZoneConfiguration oldConfiguration = mCallback.getConfiguration(userId); - final TimeZoneConfiguration newConfiguration = - new TimeZoneConfiguration.Builder(oldConfiguration) - .mergeProperties(configurationChanges) - .build(); - - // Set the configuration / notify as needed. - boolean configurationChanged = !oldConfiguration.equals(newConfiguration); - if (configurationChanged) { - mCallback.setConfiguration(userId, newConfiguration); - - String logMsg = "Configuration changed:" - + "oldConfiguration=" + oldConfiguration - + ", configuration=" + configurationChanges - + ", newConfiguration=" + newConfiguration; - mTimeZoneChangesLog.log(logMsg); - if (DBG) { - Slog.d(LOG_TAG, logMsg); - } - mListener.onConfigurationChanged(); + // Store the configuration / notify as needed. This will cause the mCallback to invoke + // handleConfigChanged() asynchronously. + mCallback.storeConfiguration(newConfiguration); + + TimeZoneConfiguration oldConfiguration = capabilities.getConfiguration(); + String logMsg = "Configuration changed:" + + " oldConfiguration=" + oldConfiguration + + ", newConfiguration=" + newConfiguration; + mTimeZoneChangesLog.log(logMsg); + if (DBG) { + Slog.d(LOG_TAG, logMsg); } return true; } - private static boolean containsAutoTimeDetectionProperties( - @NonNull TimeZoneConfiguration configuration) { - return configuration.hasProperty(PROPERTY_AUTO_DETECTION_ENABLED) - || configuration.hasProperty(PROPERTY_GEO_DETECTION_ENABLED); - } - @Override public synchronized void suggestGeolocationTimeZone( @NonNull GeolocationTimeZoneSuggestion suggestion) { + + int currentUserId = mCallback.getCurrentUserId(); + ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId); if (DBG) { - Slog.d(LOG_TAG, "Geolocation suggestion received. newSuggestion=" + suggestion); + Slog.d(LOG_TAG, "Geolocation suggestion received." + + " currentUserConfig=" + currentUserConfig + + " newSuggestion=" + suggestion); } - Objects.requireNonNull(suggestion); - mLatestGeoLocationSuggestion.set(suggestion); - // Now perform auto time zone detection. The new suggestion may be used to modify the time - // zone setting. - if (mCallback.isGeoDetectionEnabled()) { + if (currentUserConfig.getGeoDetectionEnabledBehavior()) { + // Only store a geolocation suggestion if geolocation detection is currently enabled. + mLatestGeoLocationSuggestion.set(suggestion); + + // Now perform auto time zone detection. The new suggestion may be used to modify the + // time zone setting. String reason = "New geolocation time zone suggested. suggestion=" + suggestion; - doAutoTimeZoneDetection(reason); + doAutoTimeZoneDetection(currentUserConfig, reason); } } @Override public synchronized boolean suggestManualTimeZone( @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion) { + + int currentUserId = mCallback.getCurrentUserId(); + if (userId != currentUserId) { + Slog.w(LOG_TAG, "Manual suggestion received but user != current user, userId=" + userId + + " suggestion=" + suggestion); + + // Only listen to changes from the current user. + return false; + } + Objects.requireNonNull(suggestion); String timeZoneId = suggestion.getZoneId(); String cause = "Manual time suggestion received: suggestion=" + suggestion; - TimeZoneCapabilities capabilities = mCallback.getCapabilities(userId); + TimeZoneCapabilities capabilities = getConfigurationInternal(userId).createCapabilities(); if (capabilities.getSuggestManualTimeZone() != CAPABILITY_POSSESSED) { Slog.i(LOG_TAG, "User does not have the capability needed to set the time zone manually" + ", capabilities=" + capabilities @@ -345,8 +329,12 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat @Override public synchronized void suggestTelephonyTimeZone( @NonNull TelephonyTimeZoneSuggestion suggestion) { + + int currentUserId = mCallback.getCurrentUserId(); + ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId); if (DBG) { - Slog.d(LOG_TAG, "Telephony suggestion received. newSuggestion=" + suggestion); + Slog.d(LOG_TAG, "Telephony suggestion received. currentUserConfig=" + currentUserConfig + + " newSuggestion=" + suggestion); } Objects.requireNonNull(suggestion); @@ -360,9 +348,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat // Now perform auto time zone detection. The new suggestion may be used to modify the time // zone setting. - if (!mCallback.isGeoDetectionEnabled()) { + if (!currentUserConfig.getGeoDetectionEnabledBehavior()) { String reason = "New telephony time zone suggested. suggestion=" + suggestion; - doAutoTimeZoneDetection(reason); + doAutoTimeZoneDetection(currentUserConfig, reason); } } @@ -392,15 +380,15 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat * Performs automatic time zone detection. */ @GuardedBy("this") - private void doAutoTimeZoneDetection(@NonNull String detectionReason) { - if (!mCallback.isAutoDetectionEnabled()) { - // Avoid doing unnecessary work with this (race-prone) check. + private void doAutoTimeZoneDetection( + @NonNull ConfigurationInternal currentUserConfig, @NonNull String detectionReason) { + if (!currentUserConfig.getAutoDetectionEnabledBehavior()) { + // Avoid doing unnecessary work. return; } - // Use the right suggestions based on the current configuration. This check is potentially - // race-prone until this value is set via a call to TimeZoneDetectorStrategy. - if (mCallback.isGeoDetectionEnabled()) { + // Use the right suggestions based on the current configuration. + if (currentUserConfig.getGeoDetectionEnabledBehavior()) { doGeolocationTimeZoneDetection(detectionReason); } else { doTelephonyTimeZoneDetection(detectionReason); @@ -480,35 +468,18 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time // zone ID. - String newZoneId = bestTelephonySuggestion.suggestion.getZoneId(); - if (newZoneId == null) { + String zoneId = bestTelephonySuggestion.suggestion.getZoneId(); + if (zoneId == null) { Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:" + " bestTelephonySuggestion=" + bestTelephonySuggestion + " detectionReason=" + detectionReason); return; } - String zoneId = bestTelephonySuggestion.suggestion.getZoneId(); String cause = "Found good suggestion." + ", bestTelephonySuggestion=" + bestTelephonySuggestion + ", detectionReason=" + detectionReason; - setAutoDeviceTimeZoneIfRequired(zoneId, cause); - } - - @GuardedBy("this") - private void setAutoDeviceTimeZoneIfRequired(@NonNull String newZoneId, @NonNull String cause) { - Objects.requireNonNull(newZoneId); - Objects.requireNonNull(cause); - - if (!mCallback.isAutoDetectionEnabled()) { - if (DBG) { - Slog.d(LOG_TAG, "Auto time zone detection is not enabled." - + ", newZoneId=" + newZoneId - + ", cause=" + cause); - } - return; - } - setDeviceTimeZoneIfRequired(newZoneId, cause); + setDeviceTimeZoneIfRequired(zoneId, cause); } @GuardedBy("this") @@ -582,13 +553,39 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat return findBestTelephonySuggestion(); } - @Override - public synchronized void handleAutoTimeZoneConfigChanged() { + private synchronized void handleConfigChanged() { if (DBG) { - Slog.d(LOG_TAG, "handleAutoTimeZoneConfigChanged()"); + Slog.d(LOG_TAG, "handleConfigChanged()"); + } + + clearGeolocationSuggestionIfNeeded(); + + for (ConfigurationChangeListener listener : mConfigChangeListeners) { + listener.onChange(); + } + } + + @GuardedBy("this") + private void clearGeolocationSuggestionIfNeeded() { + // This method is called whenever the user changes or the config for any user changes. We + // don't know what happened, so we capture the current user's config, check to see if we + // need to clear state associated with a previous user, and rerun detection. + int currentUserId = mCallback.getCurrentUserId(); + ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId); + + GeolocationTimeZoneSuggestion latestGeoLocationSuggestion = + mLatestGeoLocationSuggestion.get(); + if (latestGeoLocationSuggestion != null + && !currentUserConfig.getGeoDetectionEnabledBehavior()) { + // The current user's config has geodetection disabled, so clear the latest suggestion. + // This is done to ensure we only ever keep a geolocation suggestion if the user has + // said it is ok to do so. + mLatestGeoLocationSuggestion.set(null); + mTimeZoneChangesLog.log( + "clearGeolocationSuggestionIfNeeded: Cleared latest Geolocation suggestion."); } - doAutoTimeZoneDetection("handleAutoTimeZoneConfigChanged()"); + doAutoTimeZoneDetection(currentUserConfig, "clearGeolocationSuggestionIfNeeded()"); } @Override @@ -604,11 +601,14 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat ipw.println("TimeZoneDetectorStrategy:"); ipw.increaseIndent(); // level 1 - ipw.println("mCallback.isAutoDetectionEnabled()=" + mCallback.isAutoDetectionEnabled()); + int currentUserId = mCallback.getCurrentUserId(); + ipw.println("mCallback.getCurrentUserId()=" + currentUserId); + ConfigurationInternal configuration = mCallback.getConfigurationInternal(currentUserId); + ipw.println("mCallback.getConfiguration(currentUserId)=" + configuration); + ipw.println("[Capabilities=" + configuration.createCapabilities() + "]"); ipw.println("mCallback.isDeviceTimeZoneInitialized()=" + mCallback.isDeviceTimeZoneInitialized()); ipw.println("mCallback.getDeviceTimeZone()=" + mCallback.getDeviceTimeZone()); - ipw.println("mCallback.isGeoDetectionEnabled()=" + mCallback.isGeoDetectionEnabled()); ipw.println("Time zone change log:"); ipw.increaseIndent(); // level 2 diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 1536473ff0a1..4c2d0d08cd4e 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -700,8 +700,8 @@ final class AccessibilityController { touchableRegion.getBounds(touchableFrame); RectF windowFrame = mTempRectF; windowFrame.set(touchableFrame); - windowFrame.offset(-windowState.getFrameLw().left, - -windowState.getFrameLw().top); + windowFrame.offset(-windowState.getFrame().left, + -windowState.getFrame().top); matrix.mapRect(windowFrame); Region windowBounds = mTempRegion2; windowBounds.set((int) windowFrame.left, (int) windowFrame.top, @@ -730,7 +730,7 @@ final class AccessibilityController { } // Count letterbox into nonMagnifiedBounds - if (windowState.isLetterboxedForDisplayCutoutLw()) { + if (windowState.isLetterboxedForDisplayCutout()) { Region letterboxBounds = getLetterboxBounds(windowState); nonMagnifiedBounds.op(letterboxBounds, Region.Op.UNION); availableBounds.op(letterboxBounds, Region.Op.DIFFERENCE); @@ -1429,11 +1429,11 @@ final class AccessibilityController { // Account for all space in the task, whether the windows in it are // touchable or not. The modal window blocks all touches from the task's // area. - unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace, + unaccountedSpace.op(windowState.getDisplayFrame(), unaccountedSpace, Region.Op.REVERSE_DIFFERENCE); } else { // If a window has tap exclude region, we need to account it. - final Region displayRegion = new Region(windowState.getDisplayFrameLw()); + final Region displayRegion = new Region(windowState.getDisplayFrame()); final Region tapExcludeRegion = new Region(); windowState.getTapExcludeRegion(tapExcludeRegion); displayRegion.op(tapExcludeRegion, displayRegion, @@ -1470,7 +1470,7 @@ final class AccessibilityController { // Move to origin as all transforms are captured by the matrix. RectF windowFrame = mTempRectF; windowFrame.set(rect); - windowFrame.offset(-windowState.getFrameLw().left, -windowState.getFrameLw().top); + windowFrame.offset(-windowState.getFrame().left, -windowState.getFrame().top); matrix.mapRect(windowFrame); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b8b20a3e80af..56261c4fce97 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -110,9 +110,13 @@ import static android.view.WindowManager.TRANSIT_UNSET; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONTAINERS; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SWITCH; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.ActivityRecordProto.ALL_DRAWN; @@ -145,9 +149,6 @@ import static com.android.server.wm.ActivityRecordProto.WINDOW_TOKEN; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE; @@ -1096,15 +1097,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private void scheduleActivityMovedToDisplay(int displayId, Configuration config) { if (!attachedToProcess()) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.w(TAG, - "Can't report activity moved to display - client not running, activityRecord=" - + this + ", displayId=" + displayId); + ProtoLog.w(WM_DEBUG_SWITCH, "Can't report activity moved " + + "to display - client not running, activityRecord=%s, displayId=%d", + this, displayId); return; } try { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, - "Reporting activity moved to display" + ", activityRecord=" + this - + ", displayId=" + displayId + ", config=" + config); + ProtoLog.v(WM_DEBUG_SWITCH, "Reporting activity moved to " + + "display, activityRecord=%s, displayId=%d, config=%s", this, displayId, + config); mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, MoveToDisplayItem.obtain(displayId, config)); @@ -1115,14 +1116,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private void scheduleConfigurationChanged(Configuration config) { if (!attachedToProcess()) { - if (DEBUG_CONFIGURATION) Slog.w(TAG, - "Can't report activity configuration update - client not running" - + ", activityRecord=" + this); + ProtoLog.w(WM_DEBUG_CONFIGURATION, "Can't report activity configuration " + + "update - client not running, activityRecord=%s", this); return; } try { - if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: " - + config); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending new config to %s, " + + "config: %s", this, config); mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, ActivityConfigurationChangeItem.obtain(config)); @@ -1334,7 +1334,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (w == null || winHint != null && w != winHint) { return; } - final boolean surfaceReady = w.isDrawnLw() // Regular case + final boolean surfaceReady = w.isDrawn() // Regular case || w.mWinAnimator.mSurfaceDestroyDeferred // The preserved surface is still ready. || w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface. final boolean needsLetterbox = surfaceReady && w.isLetterboxedAppWindow() && fillsParent(); @@ -1355,7 +1355,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A : inMultiWindowMode() ? task.getBounds() : getRootTask().getParent().getBounds(); - mLetterbox.layout(spaceToFill, w.getFrameLw(), mTmpPoint); + mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint); } else if (mLetterbox != null) { mLetterbox.hide(); } @@ -1949,10 +1949,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A startingWindow = null; startingDisplayed = false; if (surface == null) { - ProtoLog.v(WM_DEBUG_STARTING_WINDOW, - "startingWindow was set but startingSurface==null, couldn't " - + "remove"); - + ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "startingWindow was set but " + + "startingSurface==null, couldn't remove"); return; } } else { @@ -1962,9 +1960,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } + ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Schedule remove starting %s startingWindow=%s" - + " startingView=%s Callers=%s", - this, startingWindow, startingSurface, Debug.getCallers(5)); + + " startingView=%s Callers=%s", this, startingWindow, startingSurface, + Debug.getCallers(5)); // Use the same thread to remove the window as we used to add it, as otherwise we end up @@ -2399,9 +2398,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ boolean moveFocusableActivityToTop(String reason) { if (!isFocusable()) { - if (DEBUG_FOCUS) { - Slog.d(TAG_FOCUS, "moveActivityStackToFront: unfocusable activity=" + this); - } + ProtoLog.d(WM_DEBUG_FOCUS, "moveActivityStackToFront: unfocusable " + + "activity=%s", this); return false; } @@ -2414,15 +2412,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mRootWindowContainer.getTopResumedActivity() == this && getDisplayContent().mFocusedApp == this) { - if (DEBUG_FOCUS) { - Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this); - } + ProtoLog.d(WM_DEBUG_FOCUS, "moveActivityStackToFront: already on top, " + + "activity=%s", this); return !isState(RESUMED); } - - if (DEBUG_FOCUS) { - Slog.d(TAG_FOCUS, "moveActivityStackToFront: activity=" + this); - } + ProtoLog.d(WM_DEBUG_FOCUS, "moveActivityStackToFront: activity=%s", this); stack.moveToFront(reason, task); // Report top activity change to tracking services and WM @@ -2798,10 +2792,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mRootWindowContainer.resumeFocusedStacksTopActivities(); } - if (DEBUG_CONTAINERS) { - Slog.d(TAG_CONTAINERS, "destroyIfPossible: r=" + this + " destroy returned removed=" - + activityRemoved); - } + ProtoLog.d(WM_DEBUG_CONTAINERS, "destroyIfPossible: r=%s destroy returned " + + "removed=%s", this, activityRemoved); return activityRemoved; } @@ -2859,12 +2851,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A app.removeActivity(this, true /* keepAssociation */); if (!app.hasActivities()) { mAtmService.clearHeavyWeightProcessIfEquals(app); - // Update any services we are bound to that might care about whether - // their client may have activities. - // No longer have activities, so update LRU list and oom adj. - app.updateProcessInfo(true /* updateServiceConnectionActivities */, - false /* activityChange */, true /* updateOomAdj */, - false /* addPendingTopUid */); } boolean skipDestroy = false; @@ -2941,10 +2927,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A finishActivityResults(Activity.RESULT_CANCELED, null /* resultData */, null /* resultGrants */); makeFinishingLocked(); - if (ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE) { - Slog.i(TAG_ADD_REMOVE, "Removing activity " + this + " from stack, reason=" - + reason + ", callers=" + Debug.getCallers(5)); - } + + ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing activity %s from stack, reason= %s " + + "callers=%s", this, reason, Debug.getCallers(5)); takeFromHistory(); removeTimeouts(); @@ -2984,7 +2969,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void destroyed(String reason) { removeDestroyTimeout(); - if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + this); + ProtoLog.d(WM_DEBUG_CONTAINERS, "activityDestroyedLocked: r=%s", this); if (!isState(DESTROYING, DESTROYED)) { throw new IllegalStateException( @@ -3185,12 +3170,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A remove = false; } if (remove) { - if (ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE || DEBUG_CLEANUP) { - Slog.i(TAG_ADD_REMOVE, "Removing activity " + this - + " hasSavedState=" + mHaveState + " stateNotNeeded=" + stateNotNeeded - + " finishing=" + finishing + " state=" + mState - + " callers=" + Debug.getCallers(5)); - } + ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing activity %s hasSavedState=%b " + + "stateNotNeeded=%s finishing=%b state=%s callers=%s", this, + mHaveState, stateNotNeeded, finishing, mState, Debug.getCallers(5)); if (!finishing || (app != null && app.isRemoved())) { Slog.w(TAG, "Force removing " + this + ": app died, no saved state"); EventLogTags.writeWmFinishActivity(mUserId, System.identityHashCode(this), @@ -4302,7 +4284,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { // If we are being set visible, and the starting window is not yet displayed, // then make sure it doesn't get displayed. - if (startingWindow != null && !startingWindow.isDrawnLw()) { + if (startingWindow != null && !startingWindow.isDrawn()) { startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY); startingWindow.mLegacyPolicyVisibilityAfterAnim = false; } @@ -4484,16 +4466,40 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A detachChildren(); } - if (state == RESUMED) { - mAtmService.updateBatteryStats(this, true); - mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_RESUMED); - } else if (state == PAUSED) { - mAtmService.updateBatteryStats(this, false); - mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_PAUSED); - } else if (state == STOPPED) { - mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_STOPPED); - } else if (state == DESTROYED) { - mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_DESTROYED); + switch (state) { + case RESUMED: + mAtmService.updateBatteryStats(this, true); + mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_RESUMED); + // Fall through. + case STARTED: + // Update process info while making an activity from invisible to visible, to make + // sure the process state is updated to foreground. + if (app != null) { + app.updateProcessInfo(false /* updateServiceConnectionActivities */, + true /* activityChange */, true /* updateOomAdj */, + true /* addPendingTopUid */); + } + break; + case PAUSED: + mAtmService.updateBatteryStats(this, false); + mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_PAUSED); + break; + case STOPPED: + mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_STOPPED); + break; + case DESTROYED: + mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_DESTROYED); + // Fall through. + case DESTROYING: + if (app != null && !app.hasActivities()) { + // Update any services we are bound to that might care about whether + // their client may have activities. + // No longer have activities, so update LRU list and oom adj. + app.updateProcessInfo(true /* updateServiceConnectionActivities */, + false /* activityChange */, true /* updateOomAdj */, + false /* addPendingTopUid */); + } + break; } } @@ -4804,14 +4810,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } setState(STARTED, "makeActiveIfNeeded"); - // Update process info while making an activity from invisible to visible, to make - // sure the process state is updated to foreground. - if (app != null) { - app.updateProcessInfo(false /* updateServiceConnectionActivities */, - true /* activityChange */, true /* updateOomAdj */, - true /* addPendingTopUid */); - } - try { mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, StartActivityItem.obtain()); @@ -5586,9 +5584,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (DEBUG_VISIBILITY || WM_DEBUG_ORIENTATION.isLogToLogcat()) { final boolean isAnimationSet = isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_APP_TRANSITION); - Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw() + Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawn() + ", isAnimationSet=" + isAnimationSet); - if (!w.isDrawnLw()) { + if (!w.isDrawn()) { Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController + " pv=" + w.isVisibleByPolicy() + " mDrawState=" + winAnimator.drawStateToString() @@ -5603,7 +5601,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (findMainWindow(false /* includeStartingApp */) != w) { mNumInterestingWindows++; } - if (w.isDrawnLw()) { + if (w.isDrawn()) { mNumDrawnWindows++; if (DEBUG_VISIBILITY || WM_DEBUG_ORIENTATION.isLogToLogcat()) { @@ -5616,7 +5614,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A isInterestingAndDrawn = true; } } - } else if (w.isDrawnLw()) { + } else if (w.isDrawn()) { // The starting window for this container is drawn. mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(this); startingDisplayed = true; @@ -6145,7 +6143,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (win == null) { return; } - final Rect frame = win.getRelativeFrameLw(); + final Rect frame = win.getRelativeFrame(); final int thumbnailDrawableRes = task.mUserId == mWmService.mCurrentUserId ? R.drawable.ic_account_circle : R.drawable.ic_corp_badge; @@ -6171,7 +6169,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // destination of the thumbnail header animation. If this is a full screen // window scenario, we use the whole display as the target. WindowState win = findMainWindow(); - Rect appRect = win != null ? win.getContentFrameLw() : + final Rect appRect = win != null ? win.getContentFrame() : new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight); final Rect insets = win != null ? win.getContentInsets() : null; final Configuration displayConfig = mDisplayContent.getConfiguration(); @@ -6997,27 +6995,27 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean ignoreVisibility) { final Task stack = getRootTask(); if (stack.mConfigWillChange) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Skipping config check (will change): " + this); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check " + + "(will change): %s", this); return true; } // We don't worry about activities that are finishing. if (finishing) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Configuration doesn't matter in finishing " + this); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration doesn't matter " + + "in finishing %s", this); stopFreezingScreenLocked(false); return true; } if (!ignoreVisibility && (mState == STOPPING || mState == STOPPED || !shouldBeVisible())) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Skipping config check invisible: " + this); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check " + + "invisible: %s", this); return true; } - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Ensuring correct configuration: " + this); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Ensuring correct " + + "configuration: %s", this); final int newDisplayId = getDisplayId(); final boolean displayChanged = mLastReportedDisplayId != newDisplayId; @@ -7033,8 +7031,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // the combine configurations are equal, but would otherwise differ in the override config mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration()); if (getConfiguration().equals(mTmpConfig) && !forceNewConfig && !displayChanged) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Configuration & display unchanged in " + this); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration & display " + + "unchanged in %s", this); return true; } @@ -7054,14 +7052,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // No need to relaunch or schedule new config for activity that hasn't been launched // yet. We do, however, return after applying the config to activity record, so that // it will use it for launch transaction. - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Skipping config check for initializing activity: " + this); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check for " + + "initializing activity: %s", this); return true; } if (changes == 0 && !forceNewConfig) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Configuration no differences in " + this); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration no differences in %s", + this); // There are no significant differences, so we won't relaunch but should still deliver // the new configuration to the client process. if (displayChanged) { @@ -7072,26 +7070,23 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return true; } - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Configuration changes for " + this + ", allChanges=" - + Configuration.configurationDiffToString(changes)); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration changes for %s, " + + "allChanges=%s", this, Configuration.configurationDiffToString(changes)); // If the activity isn't currently running, just leave the new configuration and it will // pick that up next time it starts. if (!attachedToProcess()) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Configuration doesn't matter not running " + this); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration doesn't matter not running %s", this); stopFreezingScreenLocked(false); forceNewConfig = false; return true; } // Figure out how to handle the changes between the configurations. - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Checking to restart " + info.name + ": changed=0x" - + Integer.toHexString(changes) + ", handles=0x" - + Integer.toHexString(info.getRealConfigChanged()) - + ", mLastReportedConfiguration=" + mLastReportedConfiguration); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Checking to restart %s: changed=0x%s, " + + "handles=0x%s, mLastReportedConfiguration=%s", info.name, + Integer.toHexString(changes), Integer.toHexString(info.getRealConfigChanged()), + mLastReportedConfiguration); if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) { // Aha, the activity isn't handling the change, so DIE DIE DIE. @@ -7108,20 +7103,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mRelaunchReason = RELAUNCH_REASON_NONE; } if (!attachedToProcess()) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Config is destroying non-running " + this); + ProtoLog.v(WM_DEBUG_CONFIGURATION, + "Config is destroying non-running %s", this); destroyImmediately("config"); } else if (mState == PAUSING) { // A little annoying: we are waiting for this activity to finish pausing. Let's not // do anything now, but just flag that it needs to be restarted when done pausing. - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Config is skipping already pausing " + this); + ProtoLog.v(WM_DEBUG_CONFIGURATION, + "Config is skipping already pausing %s", this); deferRelaunchUntilPaused = true; preserveWindowOnDeferredRelaunch = preserveWindow; return true; } else { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Config is relaunching " + this); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Config is relaunching %s", + this); if (DEBUG_STATES && !mVisibleRequested) { Slog.v(TAG_STATES, "Config is relaunching invisible activity " + this + " called by " + Debug.getCallers(4)); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 4c93b9ef64de..be7a6aed7489 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -56,12 +56,12 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.INVALID_UID; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS; @@ -116,6 +116,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.HeavyWeightSwitcherActivity; import com.android.internal.app.IVoiceInteractor; +import com.android.internal.protolog.common.ProtoLog; import com.android.server.am.PendingIntentRecord; import com.android.server.pm.InstantAppResolver; import com.android.server.power.ShutdownCheckPoints; @@ -669,10 +670,8 @@ class ActivityStarter { if (stack != null) { stack.mConfigWillChange = globalConfigWillChange; } - if (DEBUG_CONFIGURATION) { - Slog.v(TAG_CONFIGURATION, "Starting activity when config will change = " - + globalConfigWillChange); - } + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Starting activity when config " + + "will change = %b", globalConfigWillChange); final long origId = Binder.clearCallingIdentity(); @@ -695,10 +694,9 @@ class ActivityStarter { if (stack != null) { stack.mConfigWillChange = false; } - if (DEBUG_CONFIGURATION) { - Slog.v(TAG_CONFIGURATION, + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Updating to new configuration after starting activity."); - } + mService.updateConfigurationLocked(mRequest.globalConfig, null, false); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java index da0bfd67e353..3c562a6472b2 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java @@ -43,12 +43,6 @@ public class ActivityTaskManagerDebugConfig { // Enable all debug log categories for activities. private static final boolean DEBUG_ALL_ACTIVITIES = DEBUG_ALL || false; - static final boolean DEBUG_ADD_REMOVE = DEBUG_ALL_ACTIVITIES || false; - public static final boolean DEBUG_CONFIGURATION = DEBUG_ALL || false; - static final boolean DEBUG_CONTAINERS = DEBUG_ALL_ACTIVITIES || false; - static final boolean DEBUG_FOCUS = false; - static final boolean DEBUG_IMMERSIVE = DEBUG_ALL || false; - static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false; static final boolean DEBUG_PAUSE = DEBUG_ALL || false; static final boolean DEBUG_RECENTS = DEBUG_ALL || false; static final boolean DEBUG_RECENTS_TRIM_TASKS = DEBUG_RECENTS || false; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 2adaa52dfb30..6a8cbfbb5840 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -66,6 +66,10 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.TRANSIT_NONE; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK; import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR; import static com.android.server.am.ActivityManagerService.MY_PID; import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS; @@ -92,10 +96,6 @@ import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IMMERSIVE; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS; @@ -242,6 +242,7 @@ import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.TransferPipe; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.KeyguardDismissCallback; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; @@ -813,7 +814,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // in-place. updateConfigurationLocked(configuration, null, true); final Configuration globalConfig = getGlobalConfiguration(); - if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Initial config: " + globalConfig); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Initial config: %s", globalConfig); // Load resources only after the current configuration has been set. final Resources res = mContext.getResources(); @@ -1960,7 +1961,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // update associated state if we're frontmost if (r.isFocusedActivityOnDisplay()) { - if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r); + ProtoLog.d(WM_DEBUG_IMMERSIVE, "Frontmost changed immersion: %s", r); applyUpdateLockStateLocked(r); } } @@ -1974,8 +1975,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final boolean nextState = r != null && r.immersive; mH.post(() -> { if (mUpdateLock.isHeld() != nextState) { - if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, - "Applying new update lock state '" + nextState + "' for " + r); + ProtoLog.d(WM_DEBUG_IMMERSIVE, "Applying new update lock state '%s' for %s", + nextState, r); if (nextState) { mUpdateLock.acquire(); } else { @@ -2176,7 +2177,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void setFocusedStack(int stackId) { mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()"); - if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId); + ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedStack: stackId=%d", stackId); final long callingId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -2198,7 +2199,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void setFocusedTask(int taskId) { mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()"); - if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId); + ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedTask: taskId=%d", taskId); final long callingId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -3013,7 +3014,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } private void startLockTaskModeLocked(@Nullable Task task, boolean isSystemCaller) { - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "startLockTaskModeLocked: " + task); + ProtoLog.w(WM_DEBUG_LOCKTASK, "startLockTaskModeLocked: %s", task); if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) { return; } @@ -3075,8 +3076,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "updateLockTaskPackages()"); } synchronized (mGlobalLock) { - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowlisting " + userId + ":" - + Arrays.toString(packages)); + ProtoLog.w(WM_DEBUG_LOCKTASK, "Allowlisting %d:%s", userId, Arrays.toString(packages)); getLockTaskController().updateLockTaskPackages(userId, packages); } } @@ -4001,9 +4001,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration, int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) { - if (DEBUG_CONFIGURATION) Slog.v(TAG, "Report configuration: " + token + " " - + Arrays.toString(horizontalSizeConfiguration) + " " - + Arrays.toString(verticalSizeConfigurations)); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Report configuration: %s %s %s", + token, Arrays.toString(horizontalSizeConfiguration), + Arrays.toString(verticalSizeConfigurations)); synchronized (mGlobalLock) { ActivityRecord record = ActivityRecord.isInStackLocked(token); if (record == null) { @@ -4497,8 +4497,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "updateLockTaskFeatures()"); } synchronized (mGlobalLock) { - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowing features " + userId + ":0x" + - Integer.toHexString(flags)); + ProtoLog.w(WM_DEBUG_LOCKTASK, "Allowing features %d:0x%s", + userId, Integer.toHexString(flags)); getLockTaskController().updateLockTaskFeatures(userId, flags); } } @@ -5183,8 +5183,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return 0; } - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION, - "Updating global configuration to: " + values); + ProtoLog.i(WM_DEBUG_CONFIGURATION, "Updating global configuration " + + "to: %s", values); writeConfigurationChanged(changes); FrameworkStatsLog.write(FrameworkStatsLog.RESOURCE_CONFIGURATION_CHANGED, values.colorMode, @@ -5262,10 +5262,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { for (int i = pidMap.size() - 1; i >= 0; i--) { final int pid = pidMap.keyAt(i); final WindowProcessController app = pidMap.get(pid); - if (DEBUG_CONFIGURATION) { - Slog.v(TAG_CONFIGURATION, "Update process config of " - + app.mName + " to new config " + configCopy); - } + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Update process config of %s to new " + + "config %s", app.mName, configCopy); app.onConfigurationChanged(configCopy); } @@ -6563,10 +6561,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) return; if (pid == MY_PID || pid < 0) { - if (DEBUG_CONFIGURATION) { - Slog.w(TAG, + ProtoLog.w(WM_DEBUG_CONFIGURATION, "Trying to update display configuration for system/invalid process."); - } return; } synchronized (mGlobalLock) { @@ -6574,18 +6570,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mRootWindowContainer.getDisplayContent(displayId); if (displayContent == null) { // Call might come when display is not yet added or has been removed. - if (DEBUG_CONFIGURATION) { - Slog.w(TAG, "Trying to update display configuration for non-existing " - + "displayId=" + displayId); - } + ProtoLog.w(WM_DEBUG_CONFIGURATION, "Trying to update display " + + "configuration for non-existing displayId=%d", displayId); return; } final WindowProcessController process = mProcessMap.getProcess(pid); if (process == null) { - if (DEBUG_CONFIGURATION) { - Slog.w(TAG, "Trying to update display configuration for invalid " - + "process, pid=" + pid); - } + ProtoLog.w(WM_DEBUG_CONFIGURATION, "Trying to update display " + + "configuration for invalid process, pid=%d", pid); return; } process.registerDisplayConfigurationListener(displayContent); diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java index c1447553ba31..8568d5fc1d64 100644 --- a/services/core/java/com/android/server/wm/BarController.java +++ b/services/core/java/com/android/server/wm/BarController.java @@ -222,12 +222,12 @@ public class BarController { } protected boolean skipAnimation() { - return !mWin.isDrawnLw(); + return !mWin.isDrawn(); } private @StatusBarManager.WindowVisibleState int computeStateLw( boolean wasVis, boolean wasAnim, WindowState win, boolean change) { - if (win.isDrawnLw()) { + if (win.isDrawn()) { final boolean vis = win.isVisibleLw(); final boolean anim = win.isAnimatingLw(); if (mState == StatusBarManager.WINDOW_STATE_HIDING && !change && !vis) { @@ -264,7 +264,7 @@ public class BarController { } boolean checkHiddenLw() { - if (mWin != null && mWin.isDrawnLw()) { + if (mWin != null && mWin.isDrawn()) { if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) { updateStateLw(StatusBarManager.WINDOW_STATE_HIDDEN); } @@ -291,7 +291,7 @@ public class BarController { } else if (mWin == null) { if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar doesn't exist"); return false; - } else if (mWin.isDisplayedLw()) { + } else if (mWin.isDisplayed()) { if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar already visible"); return false; } else { diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java index 167afab9db0e..7e55f0aa2aa6 100644 --- a/services/core/java/com/android/server/wm/CompatModePackages.java +++ b/services/core/java/com/android/server/wm/CompatModePackages.java @@ -16,8 +16,8 @@ package com.android.server.wm; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -37,6 +37,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.Xml; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.FastXmlSerializer; import org.xmlpull.v1.XmlPullParser; @@ -333,8 +334,8 @@ public final class CompatModePackages { } try { if (app.hasThread()) { - if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc " - + app.mName + " new compat " + ci); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s " + + "new compat %s", app.mName, ci); app.getThread().updatePackageCompatibilityInfo(packageName, ci); } } catch (Exception e) { diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java index 9a397fe07f4e..01c007e381b1 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java +++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java @@ -18,6 +18,9 @@ package com.android.server.wm; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; + import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -25,6 +28,8 @@ import android.view.SurfaceControl; import android.window.IDisplayAreaOrganizer; import android.window.IDisplayAreaOrganizerController; +import com.android.internal.protolog.common.ProtoLog; + import java.util.HashMap; public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerController.Stub { @@ -67,9 +72,12 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl @Override public void registerOrganizer(IDisplayAreaOrganizer organizer, int feature) { enforceStackPermission("registerOrganizer()"); + final long uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register display organizer=%s uid=%d", + organizer.asBinder(), uid); if (mOrganizersByFeatureIds.get(feature) != null) { throw new IllegalStateException( "Replacing existing organizer currently unsupported"); @@ -96,9 +104,12 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl @Override public void unregisterOrganizer(IDisplayAreaOrganizer organizer) { enforceStackPermission("unregisterTaskOrganizer()"); + final long uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Unregister display organizer=%s uid=%d", + organizer.asBinder(), uid); mOrganizersByFeatureIds.entrySet().removeIf( entry -> entry.getValue().asBinder() == organizer.asBinder()); @@ -113,6 +124,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl } void onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea appeared name=%s", da.getName()); try { SurfaceControl outSurfaceControl = new SurfaceControl(da.getSurfaceControl(), "DisplayAreaOrganizerController.onDisplayAreaAppeared"); @@ -123,6 +135,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl } void onDisplayAreaVanished(IDisplayAreaOrganizer organizer, DisplayArea da) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea vanished name=%s", da.getName()); try { organizer.onDisplayAreaVanished(da.getDisplayAreaInfo()); } catch (RemoteException e) { @@ -131,6 +144,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl } void onDisplayAreaInfoChanged(IDisplayAreaOrganizer organizer, DisplayArea da) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea info changed name=%s", da.getName()); try { organizer.onDisplayAreaInfoChanged(da.getDisplayAreaInfo()); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 0215ead7e5de..2f7cc69b01a7 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -694,7 +694,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Don't do layout of a window if it is not visible, or soon won't be visible, to avoid // wasting time and funky changes while a window is animating away. final boolean gone = (mTmpWindow != null && mWmService.mPolicy.canBeHiddenByKeyguardLw(w)) - || w.isGoneForLayoutLw(); + || w.isGoneForLayout(); if (DEBUG_LAYOUT && !w.mLayoutAttached) { Slog.v(TAG, "1ST PASS " + w + ": gone=" + gone + " mHaveFrame=" + w.mHaveFrame @@ -742,7 +742,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (firstLayout) { // The client may compute its actual requested size according to the first layout, // so we still request the window to resize if the current frame is empty. - if (!w.getFrameLw().isEmpty()) { + if (!w.getFrame().isEmpty()) { w.updateLastFrames(); } w.updateLastInsetValues(); @@ -753,9 +753,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp w.mActivityRecord.layoutLetterbox(w); } - if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw() + if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrame() + " mContainingFrame=" + w.getContainingFrame() - + " mDisplayFrame=" + w.getDisplayFrameLw()); + + " mDisplayFrame=" + w.getDisplayFrame()); } }; @@ -780,9 +780,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp w.prelayout(); getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames); w.mLayoutSeq = mLayoutSeq; - if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw() + if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrame() + " mContainingFrame=" + w.getContainingFrame() - + " mDisplayFrame=" + w.getDisplayFrameLw()); + + " mDisplayFrame=" + w.getDisplayFrame()); } } }; @@ -807,7 +807,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured; if (!mTmpApplySurfaceChangesTransactionState.obscured) { - final boolean isDisplayed = w.isDisplayedLw(); + final boolean isDisplayed = w.isDisplayed(); if (isDisplayed && w.isObscuringDisplay()) { // This window completely covers everything behind it, so we want to leave all @@ -2549,7 +2549,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return; } - if (w.isOnScreen() && w.isVisibleLw() && w.getFrameLw().contains(x, y)) { + if (w.isOnScreen() && w.isVisibleLw() && w.getFrame().contains(x, y)) { targetWindowType[0] = w.mAttrs.type; return; } @@ -2747,7 +2747,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void adjustForImeIfNeeded() { final WindowState imeWin = mInputMethodWindow; final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() - && imeWin.isDisplayedLw(); + && imeWin.isDisplayed(); final int imeHeight = mDisplayFrames.getInputMethodWindowVisibleHeight(); mPinnedStackControllerLocked.setAdjustedForIme(imeVisible, imeHeight); } @@ -3364,7 +3364,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Now, a special case -- if the last target's window is in the process of exiting, but // not removed, keep on the last target to avoid IME flicker. The exception is if the // current target is home since we want opening apps to become the IME target right away. - if (curTarget != null && !curTarget.mRemoved && curTarget.isDisplayedLw() + if (curTarget != null && !curTarget.mRemoved && curTarget.isDisplayed() && curTarget.isClosing() && !curTarget.isActivityTypeHome()) { if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Not changing target till current window is" + " closing and not removed"); @@ -3647,7 +3647,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final WindowState visibleNotDrawnWindow = getWindow(w -> { final boolean isVisible = w.isVisible() && !w.mObscured; - final boolean isDrawn = w.isDrawnLw(); + final boolean isDrawn = w.isDrawn(); if (isVisible && !isDrawn) { ProtoLog.d(WM_DEBUG_BOOT, "DisplayContent: boot is waiting for window of type %d to be drawn", @@ -4599,9 +4599,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp DisplayContent dc = this; do { final WindowState displayParent = dc.getParentWindow(); - location.x += displayParent.getFrameLw().left + location.x += displayParent.getFrame().left + (dc.getLocationInParentWindow().x * displayParent.mGlobalScale + 0.5f); - location.y += displayParent.getFrameLw().top + location.y += displayParent.getFrame().top + (dc.getLocationInParentWindow().y * displayParent.mGlobalScale + 0.5f); dc = displayParent.getDisplayContent(); } while (dc != null && dc.getParentWindow() != null); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 40fc25b41d9f..1c147c259f07 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -195,13 +195,13 @@ import com.android.internal.widget.PointerLocationView; import com.android.server.LocalServices; import com.android.server.UiThread; import com.android.server.policy.WindowManagerPolicy; -import com.android.server.policy.WindowManagerPolicy.InputConsumer; import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition; import com.android.server.policy.WindowManagerPolicy.ScreenOnListener; import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs; import com.android.server.policy.WindowOrientationListener; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.wallpaper.WallpaperManagerInternal; +import com.android.server.wm.InputMonitor.EventReceiverInputConsumer; import com.android.server.wm.utils.InsetUtils; import java.io.PrintWriter; @@ -416,7 +416,7 @@ public class DisplayPolicy { private boolean mAllowLockscreenWhenOn; @VisibleForTesting - InputConsumer mInputConsumer = null; + EventReceiverInputConsumer mInputConsumer; private PointerLocationView mPointerLocationView; @@ -462,7 +462,7 @@ public class DisplayPolicy { } break; case MSG_DISPOSE_INPUT_CONSUMER: - disposeInputConsumer((InputConsumer) msg.obj); + disposeInputConsumer((EventReceiverInputConsumer) msg.obj); break; case MSG_ENABLE_POINTER_LOCATION: enablePointerLocation(); @@ -1126,7 +1126,7 @@ public class DisplayPolicy { // For IME we use regular frame. (displayFrames, windowState, inOutFrame) -> - inOutFrame.set(windowState.getFrameLw())); + inOutFrame.set(windowState.getFrame())); mDisplayContent.setInsetProvider(ITYPE_BOTTOM_GESTURES, win, (displayFrames, windowState, inOutFrame) -> { @@ -1203,11 +1203,11 @@ public class DisplayPolicy { // IME should not provide frame which is smaller than the nav bar frame. Otherwise, // nav bar might be overlapped with the content of the client when IME is shown. sTmpRect.set(inOutFrame); - sTmpRect.intersectUnchecked(mNavigationBar.getFrameLw()); - inOutFrame.inset(windowState.getGivenContentInsetsLw()); + sTmpRect.intersectUnchecked(mNavigationBar.getFrame()); + inOutFrame.inset(windowState.mGivenContentInsets); inOutFrame.union(sTmpRect); } else { - inOutFrame.inset(windowState.getGivenContentInsetsLw()); + inOutFrame.inset(windowState.mGivenContentInsets); } }; } @@ -2075,7 +2075,7 @@ public class DisplayPolicy { // In case we forced the window to draw behind the navigation bar, restrict df to // DF.Restricted to simulate old compat behavior. - Rect parentDisplayFrame = attached.getDisplayFrameLw(); + Rect parentDisplayFrame = attached.getDisplayFrame(); final WindowManager.LayoutParams attachedAttrs = attached.mAttrs; if ((attachedAttrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0 && (attachedAttrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 @@ -2096,14 +2096,14 @@ public class DisplayPolicy { // setting {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR} flag. // Otherwise, use the overscan frame. cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0 - ? attached.getContentFrameLw() : parentDisplayFrame); + ? attached.getContentFrame() : parentDisplayFrame); } else { // If the window is resizing, then we want to base the content frame on our attached // content frame to resize...however, things can be tricky if the attached window is // NOT in resize mode, in which case its content frame will be larger. // Ungh. So to deal with that, make sure the content frame we end up using is not // covering the IM dock. - cf.set(attached.getContentFrameLw()); + cf.set(attached.getContentFrame()); if (attached.isVoiceInteraction()) { cf.intersectUnchecked(displayFrames.mVoiceContent); } else if (win.isInputMethodTarget() || attached.isInputMethodTarget()) { @@ -2111,11 +2111,11 @@ public class DisplayPolicy { } } df.set(insetDecors ? parentDisplayFrame : cf); - vf.set(attached.getVisibleFrameLw()); + vf.set(attached.getVisibleFrame()); } // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be // positioned relative to its parent or the entire screen. - pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df); + pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df); } private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) { @@ -2217,8 +2217,8 @@ public class DisplayPolicy { vf.set(adjust != SOFT_INPUT_ADJUST_NOTHING ? displayFrames.mCurrent : displayFrames.mDock); } else { - pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df); - vf.set(attached.getVisibleFrameLw()); + pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df); + vf.set(attached.getVisibleFrame()); } cf.set(adjust != SOFT_INPUT_ADJUST_RESIZE ? displayFrames.mDock : displayFrames.mContent); @@ -2623,12 +2623,10 @@ public class DisplayPolicy { win.computeFrame(displayFrames); // Dock windows carve out the bottom of the screen, so normal windows // can't appear underneath them. - if (type == TYPE_INPUT_METHOD && win.isVisibleLw() - && !win.getGivenInsetsPendingLw()) { + if (type == TYPE_INPUT_METHOD && win.isVisibleLw() && !win.mGivenInsetsPending) { offsetInputMethodWindowLw(win, displayFrames); } - if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw() - && !win.getGivenInsetsPendingLw()) { + if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw() && !win.mGivenInsetsPending) { offsetVoiceInputWindowLw(win, displayFrames); } } @@ -2645,8 +2643,8 @@ public class DisplayPolicy { final int navBarPosition = navigationBarPosition(displayFrames.mDisplayWidth, displayFrames.mDisplayHeight, rotation); - int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); - top += win.getGivenContentInsetsLw().top; + int top = Math.max(win.getDisplayFrame().top, win.getContentFrame().top); + top += win.mGivenContentInsets.top; displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top); if (navBarPosition == NAV_BAR_BOTTOM) { // Always account for the nav bar frame height on the bottom since in all navigation @@ -2658,8 +2656,8 @@ public class DisplayPolicy { displayFrames.mUnrestricted.bottom - navFrameHeight); } displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); - top = win.getVisibleFrameLw().top; - top += win.getGivenVisibleInsetsLw().top; + top = win.getVisibleFrame().top; + top += win.mGivenVisibleInsets.top; displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top); if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom=" + displayFrames.mDock.bottom + " mContentBottom=" @@ -2667,8 +2665,8 @@ public class DisplayPolicy { } private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) { - int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); - top += win.getGivenContentInsetsLw().top; + int top = Math.max(win.getDisplayFrame().top, win.getContentFrame().top); + top += win.mGivenContentInsets.top; displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); } @@ -2729,8 +2727,7 @@ public class DisplayPolicy { if (win.isDreamWindow()) { // If the lockscreen was showing when the dream started then wait // for the dream to draw before hiding the lockscreen. - if (!mDreamingLockscreen - || (win.isVisibleLw() && win.hasDrawnLw())) { + if (!mDreamingLockscreen || (win.isVisibleLw() && win.hasDrawn())) { mShowingDream = true; appWindow = true; } @@ -2916,7 +2913,7 @@ public class DisplayPolicy { final InsetsSource request = mTopFullscreenOpaqueWindowState.getRequestedInsetsState() .peekSource(ITYPE_STATUS_BAR); if (WindowManagerDebugConfig.DEBUG) { - Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()); + Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrame()); Slog.d(TAG, "attr: " + attrs + " request: " + request); } return (fl & LayoutParams.FLAG_FULLSCREEN) != 0 @@ -3398,7 +3395,7 @@ public class DisplayPolicy { mImmersiveModeConfirmation.confirmCurrentPrompt(); } - private void disposeInputConsumer(InputConsumer inputConsumer) { + private void disposeInputConsumer(EventReceiverInputConsumer inputConsumer) { if (inputConsumer != null) { inputConsumer.dispose(); } @@ -4171,6 +4168,6 @@ public class DisplayPolicy { return false; } - return Rect.intersects(targetWindow.getFrameLw(), navBarWindow.getFrameLw()); + return Rect.intersects(targetWindow.getFrame(), navBarWindow.getFrame()); } } diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 86e2698302aa..f0f338534ed2 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -93,7 +93,7 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { || (mImeTargetFromIme != null && isImeTargetFromDisplayContentAndImeSame() && mWin != null - && mWin.isDrawnLw() + && mWin.isDrawn() && !mWin.mGivenInsetsPending)) { mIsImeLayoutDrawn = true; // show IME if InputMethodService requested it to be shown. diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 16c4942ee972..fb511e032c98 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -61,7 +61,6 @@ import android.view.InputWindowHandle; import android.view.SurfaceControl; import com.android.internal.protolog.common.ProtoLog; -import com.android.server.policy.WindowManagerPolicy; import java.io.PrintWriter; import java.util.Set; @@ -95,8 +94,11 @@ final class InputMonitor { */ private final ArrayMap<String, InputConsumerImpl> mInputConsumers = new ArrayMap(); - private static final class EventReceiverInputConsumer extends InputConsumerImpl - implements WindowManagerPolicy.InputConsumer { + /** + * Representation of a input consumer that the policy has added to the window manager to consume + * input events going to windows below it. + */ + static final class EventReceiverInputConsumer extends InputConsumerImpl { private InputMonitor mInputMonitor; private final InputEventReceiver mInputEventReceiver; @@ -111,8 +113,8 @@ final class InputMonitor { mClientChannel, looper); } - @Override - public void dismiss() { + /** Removes the input consumer from the window manager. */ + void dismiss() { synchronized (mService.mGlobalLock) { mInputMonitor.mInputConsumers.remove(mName); hide(mInputMonitor.mInputTransaction); @@ -120,8 +122,8 @@ final class InputMonitor { } } - @Override - public void dispose() { + /** Disposes the input consumer and input receiver from the associated thread. */ + void dispose() { synchronized (mService.mGlobalLock) { disposeChannelsLw(mInputMonitor.mInputTransaction); mInputEventReceiver.dispose(); @@ -225,7 +227,7 @@ final class InputMonitor { } } - WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name, + EventReceiverInputConsumer createInputConsumer(Looper looper, String name, InputEventReceiver.Factory inputEventReceiverFactory) { if (!name.contentEquals(INPUT_CONSUMER_NAVIGATION)) { throw new IllegalArgumentException("Illegal input consumer : " + name @@ -289,7 +291,7 @@ final class InputMonitor { inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures; inputWindowHandle.displayId = child.getDisplayId(); - final Rect frame = child.getFrameLw(); + final Rect frame = child.getFrame(); inputWindowHandle.frameLeft = frame.left; inputWindowHandle.frameTop = frame.top; inputWindowHandle.frameRight = frame.right; diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index d1eb79556d1d..e00c9e7ac38b 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -173,7 +173,7 @@ class InsetsSourceProvider { // frame may not yet determined that server side doesn't think the window is ready to // visible. (i.e. No surface, pending insets that were given during layout, etc..) if (mServerVisible) { - mTmpRect.set(mWin.getFrameLw()); + mTmpRect.set(mWin.getFrame()); if (mFrameProvider != null) { mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect); } else { @@ -185,14 +185,14 @@ class InsetsSourceProvider { mSource.setFrame(mTmpRect); if (mImeFrameProvider != null) { - mImeOverrideFrame.set(mWin.getFrameLw()); + mImeOverrideFrame.set(mWin.getFrame()); mImeFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mImeOverrideFrame); } if (mWin.mGivenVisibleInsets.left != 0 || mWin.mGivenVisibleInsets.top != 0 || mWin.mGivenVisibleInsets.right != 0 || mWin.mGivenVisibleInsets.bottom != 0) { - mTmpRect.set(mWin.getFrameLw()); + mTmpRect.set(mWin.getFrame()); mTmpRect.inset(mWin.mGivenVisibleInsets); mSource.setVisibleFrame(mTmpRect); } else { diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 8ef57f726658..c8d7693c9229 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -29,7 +29,7 @@ import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_CURRENT; import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -65,6 +65,7 @@ import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; @@ -448,7 +449,7 @@ public class LockTaskController { * unlike {@link #stopLockTaskMode(Task, boolean, int)}, it doesn't perform the checks. */ void clearLockedTasks(String reason) { - if (DEBUG_LOCKTASK) Slog.i(TAG_LOCKTASK, "clearLockedTasks: " + reason); + ProtoLog.i(WM_DEBUG_LOCKTASK, "clearLockedTasks: %s", reason); if (!mLockTaskModeTasks.isEmpty()) { clearLockedTask(mLockTaskModeTasks.get(0)); } @@ -490,10 +491,10 @@ public class LockTaskController { if (!mLockTaskModeTasks.remove(task)) { return; } - if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: removed " + task); + ProtoLog.d(WM_DEBUG_LOCKTASK, "removeLockedTask: removed %s", task); if (mLockTaskModeTasks.isEmpty()) { - if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: task=" + task + - " last task, reverting locktask mode. Callers=" + Debug.getCallers(3)); + ProtoLog.d(WM_DEBUG_LOCKTASK, "removeLockedTask: task=%s last task, " + + "reverting locktask mode. Callers=%s", task, Debug.getCallers(3)); mHandler.post(() -> performStopLockTask(task.mUserId)); } } @@ -558,7 +559,7 @@ public class LockTaskController { if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) { // startLockTask() called by app, but app is not part of lock task allowlist. Show // app pinning request. We will come back here with isSystemCaller true. - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user"); + ProtoLog.w(WM_DEBUG_LOCKTASK, "Mode default, asking user"); StatusBarManagerInternal statusBarManager = LocalServices.getService( StatusBarManagerInternal.class); if (statusBarManager != null) { @@ -569,8 +570,7 @@ public class LockTaskController { } // System can only initiate screen pinning, not full lock task mode - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, - isSystemCaller ? "Locking pinned" : "Locking fully"); + ProtoLog.w(WM_DEBUG_LOCKTASK, "%s", isSystemCaller ? "Locking pinned" : "Locking fully"); setLockTaskMode(task, isSystemCaller ? LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED, "startLockTask", true); } @@ -584,7 +584,7 @@ public class LockTaskController { String reason, boolean andResume) { // Should have already been checked, but do it again. if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) { - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, + ProtoLog.w(WM_DEBUG_LOCKTASK, "setLockTaskMode: Can't lock due to auth"); return; } @@ -602,8 +602,8 @@ public class LockTaskController { task.mUserId, lockTaskModeState)); } - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskMode: Locking to " + task + - " Callers=" + Debug.getCallers(4)); + ProtoLog.w(WM_DEBUG_LOCKTASK, "setLockTaskMode: Locking to %s Callers=%s", + task, Debug.getCallers(4)); if (!mLockTaskModeTasks.contains(task)) { mLockTaskModeTasks.add(task); @@ -672,8 +672,8 @@ public class LockTaskController { } // Terminate locked tasks that have recently lost allowlist authorization. - if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " + - lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString()); + ProtoLog.d(WM_DEBUG_LOCKTASK, "onLockTaskPackagesUpdated: removing %s" + + " mLockTaskAuth()=%s", lockedTask, lockedTask.lockTaskAuthToString()); removeLockedTask(lockedTask); lockedTask.performClearTaskLocked(); taskChanged = true; @@ -686,8 +686,8 @@ public class LockTaskController { if (mLockTaskModeTasks.isEmpty() && task!= null && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) { // This task must have just been authorized. - if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, - "onLockTaskPackagesUpdated: starting new locktask task=" + task); + ProtoLog.d(WM_DEBUG_LOCKTASK, "onLockTaskPackagesUpdated: starting new " + + "locktask task=%s", task); setLockTaskMode(task, LOCK_TASK_MODE_LOCKED, "package updated", false); taskChanged = true; } diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java index cc5ed36e0f47..c3953b4efa16 100644 --- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java +++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java @@ -16,9 +16,8 @@ package com.android.server.wm; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS; -import static com.android.server.wm.Task.TAG_ADD_REMOVE; import static com.android.server.wm.Task.TAG_TASKS; import android.app.ActivityOptions; @@ -27,6 +26,7 @@ import android.content.pm.ActivityInfo; import android.os.Debug; import android.util.Slog; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledFunction; import com.android.internal.util.function.pooled.PooledLambda; @@ -218,8 +218,8 @@ class ResetTargetTaskHelper { if (takeOptions) { noOptions = takeOption(p, noOptions); } - if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE, "Removing activity " + p + " from task=" - + mTask + " adding to task=" + targetTask + " Callers=" + Debug.getCallers(4)); + ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing activity %s from task=%s " + + "adding to task=%s Callers=%s", p, mTask, targetTask, Debug.getCallers(4)); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Pushing next activity " + p + " out to target's task " + target); p.reparent(targetTask, position, "resetTargetTaskIfNeeded"); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 0bf3dd9ca4a0..21e30ce0a495 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1106,14 +1106,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final WindowManager.LayoutParams attrs = w.mAttrs; final int attrFlags = attrs.flags; final boolean onScreen = w.isOnScreen(); - final boolean canBeSeen = w.isDisplayedLw(); + final boolean canBeSeen = w.isDisplayed(); final int privateflags = attrs.privateFlags; boolean displayHasContent = false; ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON, "handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w" + ".isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d", - w, w.mHasSurface, onScreen, w.isDisplayedLw(), w.mAttrs.userActivityTimeout); + w, w.mHasSurface, onScreen, w.isDisplayed(), w.mAttrs.userActivityTimeout); if (w.mHasSurface && onScreen) { if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) { mUserActivityTimeout = w.mAttrs.userActivityTimeout; @@ -3124,7 +3124,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> FinishDisabledPackageActivitiesHelper mFinishDisabledPackageActivitiesHelper = new FinishDisabledPackageActivitiesHelper(); class FinishDisabledPackageActivitiesHelper { - private boolean mDidSomething; private String mPackageName; private Set<String> mFilterByClasses; private boolean mDoit; @@ -3132,11 +3131,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> private int mUserId; private boolean mOnlyRemoveNoProcess; private Task mLastTask; - private ComponentName mHomeActivity; + private final ArrayList<ActivityRecord> mCollectedActivities = new ArrayList<>(); private void reset(String packageName, Set<String> filterByClasses, boolean doit, boolean evenPersistent, int userId, boolean onlyRemoveNoProcess) { - mDidSomething = false; mPackageName = packageName; mFilterByClasses = filterByClasses; mDoit = doit; @@ -3144,7 +3142,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mUserId = userId; mOnlyRemoveNoProcess = onlyRemoveNoProcess; mLastTask = null; - mHomeActivity = null; } boolean process(String packageName, Set<String> filterByClasses, @@ -3152,14 +3149,35 @@ class RootWindowContainer extends WindowContainer<DisplayContent> reset(packageName, filterByClasses, doit, evenPersistent, userId, onlyRemoveNoProcess); final PooledFunction f = PooledLambda.obtainFunction( - FinishDisabledPackageActivitiesHelper::processActivity, this, + FinishDisabledPackageActivitiesHelper::collectActivity, this, PooledLambda.__(ActivityRecord.class)); forAllActivities(f); f.recycle(); - return mDidSomething; + + boolean didSomething = false; + final int size = mCollectedActivities.size(); + // Keep the finishing order from top to bottom. + for (int i = 0; i < size; i++) { + final ActivityRecord r = mCollectedActivities.get(i); + if (mOnlyRemoveNoProcess) { + if (!r.hasProcess()) { + didSomething = true; + Slog.i(TAG, " Force removing " + r); + r.cleanUp(false /* cleanServices */, false /* setState */); + r.removeFromHistory("force-stop"); + } + } else { + didSomething = true; + Slog.i(TAG, " Force finishing " + r); + r.finishIfPossible("force-stop", true /* oomAdj */); + } + } + mCollectedActivities.clear(); + + return didSomething; } - private boolean processActivity(ActivityRecord r) { + private boolean collectActivity(ActivityRecord r) { final boolean sameComponent = (r.packageName.equals(mPackageName) && (mFilterByClasses == null || mFilterByClasses.contains(r.mActivityComponent.getClassName()))) @@ -3176,26 +3194,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } return true; } - if (r.isActivityTypeHome()) { - if (mHomeActivity != null && mHomeActivity.equals(r.mActivityComponent)) { - Slog.i(TAG, "Skip force-stop again " + r); - return false; - } else { - mHomeActivity = r.mActivityComponent; - } - } - if (mOnlyRemoveNoProcess) { - if (noProcess) { - mDidSomething = true; - Slog.i(TAG, " Force removing " + r); - r.cleanUp(false /* cleanServices */, false /* setState */); - r.removeFromHistory("force-stop"); - } - } else { - mDidSomething = true; - Slog.i(TAG, " Force finishing " + r); - r.finishIfPossible("force-stop", true /* oomAdj */); - } + mCollectedActivities.add(r); mLastTask = r.getTask(); } diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 6cf9432089b4..7b5b0ad870dd 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -99,9 +99,8 @@ class RunningTasks { // the task's profile return; } - if (!mAllowed && !task.isActivityTypeHome()) { - // Skip if the caller isn't allowed to fetch this task, except for the home - // task which we always return. + if (!mAllowed) { + // Skip if the caller isn't allowed to fetch this task return; } } diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 33935d61ead2..7df2b407557d 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -26,6 +26,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.util.DebugUtils; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; @@ -429,7 +430,8 @@ class SurfaceAnimator { void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mLeash="); pw.print(mLeash); - pw.print(" mAnimationType=" + mAnimationType); + pw.print(" mAnimationType=" + DebugUtils.valueToString(SurfaceAnimator.class, + "ANIMATION_TYPE_", mAnimationType)); pw.println(mAnimationStartDelayed ? " mAnimationStartDelayed=true" : ""); pw.print(prefix); pw.print("Animation: "); pw.println(mAnimation); if (mAnimation != null) { @@ -442,56 +444,56 @@ class SurfaceAnimator { * No animation is specified. * @hide */ - static final int ANIMATION_TYPE_NONE = 0; + public static final int ANIMATION_TYPE_NONE = 0; /** * Animation for an app transition. * @hide */ - static final int ANIMATION_TYPE_APP_TRANSITION = 1; + public static final int ANIMATION_TYPE_APP_TRANSITION = 1; /** * Animation for screen rotation. * @hide */ - static final int ANIMATION_TYPE_SCREEN_ROTATION = 1 << 1; + public static final int ANIMATION_TYPE_SCREEN_ROTATION = 1 << 1; /** * Animation for dimming. * @hide */ - static final int ANIMATION_TYPE_DIMMER = 1 << 2; + public static final int ANIMATION_TYPE_DIMMER = 1 << 2; /** * Animation for recent apps. * @hide */ - static final int ANIMATION_TYPE_RECENTS = 1 << 3; + public static final int ANIMATION_TYPE_RECENTS = 1 << 3; /** * Animation for a {@link WindowState} without animating the activity. * @hide */ - static final int ANIMATION_TYPE_WINDOW_ANIMATION = 1 << 4; + public static final int ANIMATION_TYPE_WINDOW_ANIMATION = 1 << 4; /** * Animation to control insets. This is actually not an animation, but is used to give the * client a leash over the system window causing insets. * @hide */ - static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5; + public static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5; /** * Animation when a fixed rotation transform is applied to a window token. * @hide */ - static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6; + public static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6; /** * Bitmask to include all animation types. This is NOT an {@link AnimationType} * @hide */ - static final int ANIMATION_TYPE_ALL = -1; + public static final int ANIMATION_TYPE_ALL = -1; /** * The type of the animation. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 4dbbbc6de32c..19bf451cec05 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -79,6 +79,7 @@ import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP; import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN; import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME; @@ -87,9 +88,7 @@ import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS; import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList; import static com.android.server.wm.ActivityStackSupervisor.printThisActivity; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; @@ -1651,8 +1650,8 @@ class Task extends WindowContainer<WindowContainer> { * Reorder the history stack so that the passed activity is brought to the front. */ final void moveActivityToFrontLocked(ActivityRecord newTop) { - if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE, "Removing and adding activity " - + newTop + " to stack at top callers=" + Debug.getCallers(4)); + ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing and adding activity %s to stack at top " + + "callers=%s", newTop, Debug.getCallers(4)); positionChildAtTop(newTop); updateEffectiveIntent(); @@ -1951,8 +1950,8 @@ class Task extends WindowContainer<WindowContainer> { ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE; break; } - if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this - + " mLockTaskAuth=" + lockTaskAuthToString()); + ProtoLog.d(WM_DEBUG_LOCKTASK, "setLockTaskAuth: task=%s mLockTaskAuth=%s", this, + lockTaskAuthToString()); } @Override @@ -6190,10 +6189,6 @@ class Task extends WindowContainer<WindowContainer> { next.setState(RESUMED, "resumeTopActivityInnerLocked"); - next.app.updateProcessInfo(false /* updateServiceConnectionActivities */, - true /* activityChange */, true /* updateOomAdj */, - true /* addPendingTopUid */); - // Have the window manager re-evaluate the orientation of // the screen based on the new activity order. boolean notUpdated = true; @@ -6374,7 +6369,8 @@ class Task extends WindowContainer<WindowContainer> { // Here it is! Now, if this is not yet visible (occluded by another task) to the // user, then just add it without starting; it will get started when the user // navigates back to it. - if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task " + task, + ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s " + + "callers: %s", r, task, new RuntimeException("here").fillInStackTrace()); rTask.positionChildAtTop(r); ActivityOptions.abort(options); @@ -6396,8 +6392,8 @@ class Task extends WindowContainer<WindowContainer> { task = activityTask; // Slot the activity into the history stack and proceed - if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task, - new RuntimeException("here").fillInStackTrace()); + ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to stack to task %s " + + "callers: %s", r, task, new RuntimeException("here").fillInStackTrace()); task.positionChildAtTop(r); // The transition animation and starting window are not needed if {@code allowMoveToFront} diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index f8465ddc02a2..63a595e3bc17 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS; import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS; @@ -37,19 +38,20 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import android.util.SparseBooleanArray; import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; import android.window.WindowContainerToken; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.LinkedHashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -126,6 +128,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } void onTaskAppeared(Task task) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId); final boolean visible = task.isVisible(); final RunningTaskInfo taskInfo = task.getTaskInfo(); mDeferTaskOrgCallbacksConsumer.accept(() -> { @@ -147,6 +150,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { void onTaskVanished(Task task) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task vanished taskId=%d", task.mTaskId); final RunningTaskInfo taskInfo = task.getTaskInfo(); mDeferTaskOrgCallbacksConsumer.accept(() -> { try { @@ -163,6 +167,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { // by the organizer that don't receive that signal return; } + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId); mDeferTaskOrgCallbacksConsumer.accept(() -> { if (!task.isOrganized()) { // This is safe to ignore if the task is no longer organized @@ -177,6 +182,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } void onBackPressedOnTaskRoot(Task task) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task back pressed on root taskId=%d", + task.mTaskId); if (!task.mCreatedByOrganizer && !task.mTaskAppearedSent) { // Skip if the task has not yet received taskAppeared(), except for tasks created // by the organizer that don't receive that signal @@ -201,7 +208,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final DeathRecipient mDeathRecipient; private final ArrayList<Task> mOrganizedTasks = new ArrayList<>(); private final int mUid; - private boolean mInterceptBackPressedOnTaskRoot; TaskOrganizerState(ITaskOrganizer organizer, int uid) { final Consumer<Runnable> deferTaskOrgCallbacksConsumer = @@ -219,10 +225,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mUid = uid; } - void setInterceptBackPressedOnTaskRoot(boolean interceptBackPressed) { - mInterceptBackPressedOnTaskRoot = interceptBackPressed; - } - void addTask(Task t) { if (t.mTaskAppearedSent) return; @@ -242,6 +244,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mOrganizer.onTaskVanished(t); } mOrganizedTasks.remove(t); + mInterceptBackPressedOnRootTasks.remove(t.mTaskId); } void dispose() { @@ -273,6 +276,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>(); private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>(); + // Set of organized tasks (by taskId) that dispatch back pressed to their organizers + private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet(); private final ActivityTaskManagerService mService; @@ -306,6 +311,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d", + organizer.asBinder(), uid); for (int winMode : SUPPORTED_WINDOWING_MODES) { if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) { mTaskOrganizers.add(organizer); @@ -327,6 +334,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { @Override public void unregisterTaskOrganizer(ITaskOrganizer organizer) { enforceStackPermission("unregisterTaskOrganizer()"); + final int uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -334,6 +342,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { if (state == null) { return; } + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Unregister task organizer=%s uid=%d", + organizer.asBinder(), uid); state.unlinkDeath(); state.dispose(); } @@ -383,6 +393,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return null; } + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create root task displayId=%d winMode=%d", + displayId, windowingMode); final Task task = display.getDefaultTaskDisplayArea().createStack(windowingMode, ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(), true /* createdByOrganizer */); @@ -407,6 +419,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { throw new IllegalArgumentException( "Attempt to delete task not created by organizer task=" + task); } + + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete root task display=%d winMode=%d", + task.getDisplayId(), task.getWindowingMode()); task.removeImmediately(); return true; } @@ -608,15 +623,23 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } @Override - public void setInterceptBackPressedOnTaskRoot(ITaskOrganizer organizer, + public void setInterceptBackPressedOnTaskRoot(WindowContainerToken token, boolean interceptBackPressed) { enforceStackPermission("setInterceptBackPressedOnTaskRoot()"); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); - if (state != null) { - state.setInterceptBackPressedOnTaskRoot(interceptBackPressed); + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set intercept back pressed on root=%b", + interceptBackPressed); + final Task task = WindowContainer.fromBinder(token.asBinder()).asTask(); + if (task == null) { + Slog.w(TAG, "Could not resolve task from token"); + return; + } + if (interceptBackPressed) { + mInterceptBackPressedOnRootTasks.add(task.mTaskId); + } else { + mInterceptBackPressedOnRootTasks.remove(task.mTaskId); } } } finally { @@ -625,15 +648,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } public boolean handleInterceptBackPressedOnTaskRoot(Task task) { - if (task == null || !task.isOrganized()) { + if (task == null || !task.isOrganized() + || !mInterceptBackPressedOnRootTasks.contains(task.mTaskId)) { return false; } final TaskOrganizerState state = mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder()); - if (!state.mInterceptBackPressedOnTaskRoot) { - return false; - } - state.mOrganizer.onBackPressedOnTaskRoot(task); return true; } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index dbbb7ff69b3b..e3112efdead2 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -483,7 +483,7 @@ class TaskSnapshotController { final InsetsState insetsState = new InsetsState(insetsPolicy.getInsetsForDispatch(mainWindow)); mergeInsetsSources(insetsState, mainWindow.getRequestedInsetsState()); - final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrameLw(), insetsState); + final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState); final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags, attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(), mHighResTaskSnapshotScale, insetsState); diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 6e00ab4939fa..ce138674cb93 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -308,7 +308,7 @@ class WallpaperController { float defaultWallpaperX = wallpaperWin.isRtl() ? 1f : 0f; float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : defaultWallpaperX; float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f; - int availw = wallpaperWin.getFrameLw().right - wallpaperWin.getFrameLw().left - dw; + int availw = wallpaperWin.getFrame().right - wallpaperWin.getFrame().left - dw; int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0; if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) { offset += mLastWallpaperDisplayOffsetX; @@ -323,7 +323,7 @@ class WallpaperController { float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f; float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f; - int availh = wallpaperWin.getFrameLw().bottom - wallpaperWin.getFrameLw().top - dh; + int availh = wallpaperWin.getFrame().bottom - wallpaperWin.getFrame().top - dh; offset = availh > 0 ? -(int)(availh * wpy + .5f) : 0; if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) { offset += mLastWallpaperDisplayOffsetY; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 2ce16b2fdd79..c45ccb6e17e3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1848,7 +1848,7 @@ public class WindowManagerService extends IWindowManager.Stub } // We use the visible frame, because we want the animation to morph the window from what // was visible to the user to the final destination of the new window. - Rect frame = replacedWindow.getVisibleFrameLw(); + Rect frame = replacedWindow.getVisibleFrame(); // We treat this as if this activity was opening, so we can trigger the app transition // animation and piggy-back on existing transition animation infrastructure. final DisplayContent dc = activity.getDisplayContent(); @@ -2069,7 +2069,7 @@ public class WindowManagerService extends IWindowManager.Stub outDisplayFrame.setEmpty(); return; } - outDisplayFrame.set(win.getDisplayFrameLw()); + outDisplayFrame.set(win.getDisplayFrame()); if (win.inSizeCompatMode()) { outDisplayFrame.scale(win.mInvGlobalScale); } @@ -2389,7 +2389,7 @@ public class WindowManagerService extends IWindowManager.Stub if (displayPolicy.areSystemBarsForcedShownLw(win)) { result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS; } - if (!win.isGoneForLayoutLw()) { + if (!win.isGoneForLayout()) { win.mResizedWhileGone = false; } @@ -2419,7 +2419,7 @@ public class WindowManagerService extends IWindowManager.Stub win.getInsetsForRelayout(outContentInsets, outVisibleInsets, outStableInsets); outCutout.set(win.getWmDisplayCutout().getDisplayCutout()); - outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw())); + outBackdropFrame.set(win.getBackdropFrame(win.getFrame())); outInsetsState.set(win.getInsetsState(), win.isClientLocal()); if (DEBUG) { Slog.v(TAG_WM, "Relayout given client " + client.asBinder() @@ -2883,11 +2883,6 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public WindowManagerPolicy.WindowState getInputMethodWindowLw() { - return mRoot.getCurrentInputMethodWindow(); - } - - @Override public void notifyKeyguardTrustedChanged() { mAtmInternal.notifyKeyguardTrustedChanged(); } @@ -5481,7 +5476,7 @@ public class WindowManagerService extends IWindowManager.Stub // Window has been removed or hidden; no draw will now happen, so stop waiting. ProtoLog.w(WM_DEBUG_SCREEN_ON, "Aborted waiting for drawn: %s", win); container.mWaitingForDrawn.remove(win); - } else if (win.hasDrawnLw()) { + } else if (win.hasDrawn()) { // Window is now drawn (and shown). ProtoLog.d(WM_DEBUG_SCREEN_ON, "Window drawn win=%s", win); container.mWaitingForDrawn.remove(win); @@ -7356,7 +7351,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mGlobalLock) { WindowState windowState = mWindowMap.get(token); if (windowState != null) { - outBounds.set(windowState.getFrameLw()); + outBounds.set(windowState.getFrame()); } else { outBounds.setEmpty(); } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index d25a64890337..c7cad2f76486 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.READ_FRAME_BUFFER; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; @@ -45,6 +46,7 @@ import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledLambda; @@ -127,6 +129,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (callback != null) { syncId = startSyncWithOrganizer(callback); } + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", + syncId); mService.deferWindowLayout(); try { ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>(); @@ -427,6 +431,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub @VisibleForTesting void setSyncReady(int id) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set sync ready, syncId=%d", id); mBLASTSyncEngine.setReady(id); } @@ -436,9 +441,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } @Override - public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) { + public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Transaction ready, syncId=%d", syncId); final IWindowContainerTransactionCallback callback = - mTransactionCallbacksByPendingSyncId.get(mSyncId); + mTransactionCallbacksByPendingSyncId.get(syncId); SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction(); for (WindowContainer container : windowContainersReady) { @@ -446,14 +452,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } try { - callback.onTransactionReady(mSyncId, mergedTransaction); + callback.onTransactionReady(syncId, mergedTransaction); } catch (RemoteException e) { // If there's an exception when trying to send the mergedTransaction to the client, we // should immediately apply it here so the transactions aren't lost. mergedTransaction.apply(); } - mTransactionCallbacksByPendingSyncId.remove(mSyncId); + mTransactionCallbacksByPendingSyncId.remove(syncId); } @Override diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index bb9cf2e2ac05..c5ebace78261 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -22,9 +22,9 @@ import static android.os.Build.VERSION_CODES.Q; import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.view.Display.INVALID_DISPLAY; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.server.am.ActivityManagerService.MY_PID; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE; @@ -72,6 +72,7 @@ import android.view.IRemoteAnimationRunner; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.HeavyWeightSwitcherActivity; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.Watchdog; import com.android.server.wm.ActivityTaskManagerService.HotPath; @@ -1348,9 +1349,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } return; } - if (DEBUG_CONFIGURATION) { - Slog.v(TAG_CONFIGURATION, "Sending to proc " + mName + " new config " + config); - } + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s new config %s", mName, + config); if (Build.IS_DEBUGGABLE && mHasImeService) { // TODO (b/135719017): Temporary log for debugging IME service. Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 69d38219284f..9ff33b18cb89 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1009,8 +1009,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP getDisplayContent().reapplyMagnificationSpec(); } - @Override - public int getOwningUid() { + /** Returns the uid of the app that owns this window. */ + int getOwningUid() { return mOwnerUid; } @@ -1024,8 +1024,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mOwnerCanAddInternalSystemWindow; } - @Override - public boolean canAcquireSleepToken() { + /** + * Returns {@code true} if the window owner has the permission to acquire a sleep token when + * it's visible. That is, they have the permission + * {@link androidManifest.permission#DEVICE_POWER}. + */ + boolean canAcquireSleepToken() { return mSession.mCanAcquireSleepToken; } @@ -1046,7 +1050,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP void computeFrame(DisplayFrames displayFrames) { getLayoutingWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout); - computeFrameLw(); + computeFrame(); // Update the source frame to provide insets to other windows during layout. If the // simulated frames exist, then this is not computing a stable result so just skip. if (mControllableInsetProvider != null && mSimulatedWindowFrames == null) { @@ -1054,8 +1058,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - @Override - public void computeFrameLw() { + /** + * Perform standard frame computation. The result can be obtained with getFrame() if so desired. + */ + void computeFrame() { if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) { // This window is being replaced and either already got information that it's being // removed or we are still waiting for some information. Because of this we don't @@ -1283,32 +1289,41 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - @Override - public Rect getFrameLw() { + /** Retrieves the current frame of the window that the application sees. */ + Rect getFrame() { return mWindowFrames.mFrame; } /** Accessor for testing */ - Rect getRelativeFrameLw() { + Rect getRelativeFrame() { return mWindowFrames.mRelFrame; } - @Override - public Rect getDisplayFrameLw() { + /** Retrieves the frame of the display that this window was last laid out in. */ + Rect getDisplayFrame() { return mWindowFrames.mDisplayFrame; } - @Override - public Rect getContentFrameLw() { + /** + * Retrieves the frame of the content area that this window was last laid out in. This is the + * area in which the content of the window should be placed. It will be smaller than the display + * frame to account for screen decorations such as a status bar or soft keyboard. + */ + Rect getContentFrame() { return mWindowFrames.mContentFrame; } - @Override - public Rect getVisibleFrameLw() { + /** + * Retrieves the frame of the visible area that this window was last laid out in. This is the + * area of the screen in which the window will actually be fully visible. It will be smaller + * than the content frame to account for transient UI elements blocking it such as an input + * method's candidates UI. + */ + Rect getVisibleFrame() { return mWindowFrames.mVisibleFrame; } - Rect getStableFrameLw() { + Rect getStableFrame() { return mWindowFrames.mStableFrame; } @@ -1337,32 +1352,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } @Override - public boolean getGivenInsetsPendingLw() { - return mGivenInsetsPending; - } - - @Override - public Rect getGivenContentInsetsLw() { - return mGivenContentInsets; - } - - @Override - public Rect getGivenVisibleInsetsLw() { - return mGivenVisibleInsets; - } - - @Override public WindowManager.LayoutParams getAttrs() { return mAttrs; } - @Override - public int getSystemUiVisibility() { + /** Retrieves the current system UI visibility flags associated with this window. */ + int getSystemUiVisibility() { return mSystemUiVisibility; } - @Override - public int getSurfaceLayer() { + /** Gets the layer at which this window's surface will be Z-ordered. */ + int getSurfaceLayer() { return mLayer; } @@ -1376,8 +1376,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mActivityRecord != null ? mActivityRecord.appToken : null; } - @Override - public boolean isVoiceInteraction() { + /** Returns true if this window is participating in voice interaction. */ + boolean isVoiceInteraction() { return mActivityRecord != null && mActivityRecord.mVoiceInteraction; } @@ -1391,7 +1391,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ void updateResizingWindowIfNeeded() { final WindowStateAnimator winAnimator = mWinAnimator; - if (!mHasSurface || getDisplayContent().mLayoutSeq != mLayoutSeq || isGoneForLayoutLw()) { + if (!mHasSurface || getDisplayContent().mLayoutSeq != mLayoutSeq || isGoneForLayout()) { return; } @@ -1464,7 +1464,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWmService.mResizingWindows.add(this); } } else if (getOrientationChanging()) { - if (isDrawnLw()) { + if (isDrawn()) { ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation not waiting for draw in %s, surfaceController %s", this, winAnimator.mSurfaceController); @@ -1639,9 +1639,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP : DEFAULT_DISPATCHING_TIMEOUT_MILLIS; } - @Override - public boolean hasAppShownWindows() { - return mActivityRecord != null && (mActivityRecord.firstWindowDrawn || mActivityRecord.startingDisplayed); + /** + * Returns true if, at any point, the application token associated with this window has actually + * displayed any windows. This is most useful with the "starting up" window to determine if any + * windows were displayed when it is closed. + * + * @return {@code true} if one or more windows have been displayed, else false. + */ + boolean hasAppShownWindows() { + return mActivityRecord != null + && (mActivityRecord.firstWindowDrawn || mActivityRecord.startingDisplayed); } boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { @@ -1663,7 +1670,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override boolean hasContentToDisplay() { - if (!mAppFreezing && isDrawnLw() && (mViewVisibility == View.VISIBLE + if (!mAppFreezing && isDrawn() && (mViewVisibility == View.VISIBLE || (isAnimating(TRANSITION | PARENTS) && !getDisplayContent().mAppTransition.isTransitionSet()))) { return true; @@ -1851,10 +1858,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * Like isOnScreen, but returns false if the surface hasn't yet * been drawn. */ - @Override - public boolean isDisplayedLw() { + boolean isDisplayed() { final ActivityRecord atoken = mActivityRecord; - return isDrawnLw() && isVisibleByPolicy() + return isDrawn() && isVisibleByPolicy() && ((!isParentWindowHidden() && (atoken == null || atoken.mVisibleRequested)) || isAnimating(TRANSITION | PARENTS)); } @@ -1867,8 +1873,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return isAnimating(TRANSITION | PARENTS); } - @Override - public boolean isGoneForLayoutLw() { + /** Returns {@code true} if this window considered to be gone for purposes of layout. */ + boolean isGoneForLayout() { final ActivityRecord atoken = mActivityRecord; return mViewVisibility == View.GONE || !mRelayoutCalled @@ -1895,11 +1901,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } /** - * Returns true if the window has a surface that it has drawn a - * complete UI in to. + * Returns true if the window has a surface that it has drawn a complete UI in to. Note that + * this is different from {@link #hasDrawn()} in that it also returns true if the window is + * READY_TO_SHOW, but was not yet promoted to HAS_DRAWN. */ - @Override - public boolean isDrawnLw() { + boolean isDrawn() { return mHasSurface && !mDestroying && (mWinAnimator.mDrawState == READY_TO_SHOW || mWinAnimator.mDrawState == HAS_DRAWN); } @@ -1914,7 +1920,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // to determine if it's occluding apps. return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE) || (mIsWallpaper && mWallpaperVisible)) - && isDrawnLw() && !isAnimating(TRANSITION | PARENTS); + && isDrawn() && !isAnimating(TRANSITION | PARENTS); } /** @see WindowManagerInternal#waitForAllWindowsDrawn */ @@ -1929,7 +1935,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } if (mAttrs.type == TYPE_APPLICATION_STARTING) { - if (isDrawnLw()) { + if (isDrawn()) { // Unnecessary to redraw a drawn starting window. return; } @@ -2016,11 +2022,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override void onResize() { final ArrayList<WindowState> resizingWindows = mWmService.mResizingWindows; - if (mHasSurface && !isGoneForLayoutLw() && !resizingWindows.contains(this)) { + if (mHasSurface && !isGoneForLayout() && !resizingWindows.contains(this)) { ProtoLog.d(WM_DEBUG_RESIZE, "onResize: Resizing %s", this); resizingWindows.add(this); } - if (isGoneForLayoutLw()) { + if (isGoneForLayout()) { mResizedWhileGone = true; } @@ -2514,7 +2520,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP /** Returns true if the replacement window was removed. */ boolean removeReplacedWindowIfNeeded(WindowState replacement) { - if (mWillReplaceWindow && mReplacementWindow == replacement && replacement.hasDrawnLw()) { + if (mWillReplaceWindow && mReplacementWindow == replacement && replacement.hasDrawn()) { replacement.mSkipEnterAnimationForSeamlessReplacement = false; removeReplacedWindow(); return true; @@ -2748,7 +2754,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mLayoutNeeded = true; } - if (isDrawnLw() && mToken.okToAnimate()) { + if (isDrawn() && mToken.okToAnimate()) { mWinAnimator.applyEnterAnimationLocked(); } } @@ -2873,8 +2879,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return getWindowConfiguration().keepVisibleDeadAppWindowOnScreen(); } - @Override - public boolean canReceiveKeys() { + /** Returns {@code true} if this window desires key events. */ + boolean canReceiveKeys() { return canReceiveKeys(false /* fromUserTouch */); } @@ -2923,8 +2929,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP && recentsAnimationController.shouldApplyInputConsumer(mActivityRecord); } - @Override - public boolean hasDrawnLw() { + /** + * Returns {@code true} if this window has been shown on screen at some time in the past. + * + * @deprecated Use {@link #isDrawnLw} or any of the other drawn/visibility methods. + */ + @Deprecated + boolean hasDrawn() { return mWinAnimator.mDrawState == WindowStateAnimator.HAS_DRAWN; } @@ -3158,8 +3169,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - @Override - public boolean isAlive() { + /** Checks whether the process hosting this window is currently alive. */ + boolean isAlive() { return mClient.asBinder().isBinderAlive(); } @@ -3339,16 +3350,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mLastExclusionLogUptimeMillis[EXCLUSION_RIGHT] = now; } - @Override - public boolean isDefaultDisplay() { - final DisplayContent displayContent = getDisplayContent(); - if (displayContent == null) { - // Only a window that was on a non-default display can be detached from it. - return false; - } - return displayContent.isDefaultDisplay; - } - /** @return {@code true} if this window can be shown to all users. */ boolean showForAllUsers() { @@ -3408,10 +3409,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // All window frames that are fullscreen extend above status bar, but some don't extend // below navigation bar. Thus, check for display frame for top/left and stable frame for // bottom right. - if (win.getFrameLw().left <= win.getDisplayFrameLw().left - && win.getFrameLw().top <= win.getDisplayFrameLw().top - && win.getFrameLw().right >= win.getStableFrameLw().right - && win.getFrameLw().bottom >= win.getStableFrameLw().bottom) { + if (win.getFrame().left <= win.getDisplayFrame().left + && win.getFrame().top <= win.getDisplayFrame().top + && win.getFrame().right >= win.getStableFrame().right + && win.getFrame().bottom >= win.getStableFrame().bottom) { // Is a fullscreen window, like the clock alarm. Show to everyone. return true; } @@ -3781,11 +3782,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * is transitioning into/out-of fullscreen. */ boolean isLetterboxedAppWindow() { return !inMultiWindowMode() && !matchesDisplayBounds() - || isLetterboxedForDisplayCutoutLw(); + || isLetterboxedForDisplayCutout(); } - @Override - public boolean isLetterboxedForDisplayCutoutLw() { + /** Returns {@code true} if the window is letterboxed for the display cutout. */ + boolean isLetterboxedForDisplayCutout() { if (mActivityRecord == null) { // Only windows with an ActivityRecord are letterboxed. return false; @@ -3889,7 +3890,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // background. return (getDisplayContent().mDividerControllerLocked.isResizing() || mActivityRecord != null && !mActivityRecord.mFrozenBounds.isEmpty()) && - !task.inFreeformWindowingMode() && !isGoneForLayoutLw(); + !task.inFreeformWindowingMode() && !isGoneForLayout(); } @@ -4305,7 +4306,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private boolean isParentWindowGoneForLayout() { final WindowState parent = getParentWindow(); - return parent != null && parent.isGoneForLayoutLw(); + return parent != null && parent.isGoneForLayout(); } void setWillReplaceWindow(boolean animate) { @@ -4418,8 +4419,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return null; } - @Override - public int getRotationAnimationHint() { + int getRotationAnimationHint() { if (mActivityRecord != null) { return mActivityRecord.mRotationAnimationHint; } else { @@ -4875,7 +4875,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } boolean hasVisibleNotDrawnWallpaper() { - if (mWallpaperVisible && !isDrawnLw()) { + if (mWallpaperVisible && !isDrawn()) { return true; } for (int j = mChildren.size() - 1; j >= 0; --j) { @@ -4899,9 +4899,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } if (DEBUG_VISIBILITY) { - Slog.v(TAG, "Win " + this + ": isDrawn=" + isDrawnLw() + Slog.v(TAG, "Win " + this + ": isDrawn=" + isDrawn() + ", animating=" + isAnimating(TRANSITION | PARENTS)); - if (!isDrawnLw()) { + if (!isDrawn()) { Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController + " pv=" + isVisibleByPolicy() + " mDrawState=" + mWinAnimator.mDrawState @@ -4912,7 +4912,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } results.numInteresting++; - if (isDrawnLw()) { + if (isDrawn()) { results.numDrawn++; if (!isAnimating(TRANSITION | PARENTS)) { results.numVisible++; @@ -5048,7 +5048,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP int relayoutVisibleWindow(int result, int attrChanges) { final boolean wasVisible = isVisibleLw(); - result |= (!wasVisible || !isDrawnLw()) ? RELAYOUT_RES_FIRST_TIME : 0; + result |= (!wasVisible || !isDrawn()) ? RELAYOUT_RES_FIRST_TIME : 0; if (mAnimatingExit) { Slog.d(TAG, "relayoutVisibleWindow: " + this + " mAnimatingExit=true, mRemoveOnExit=" @@ -5620,15 +5620,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return !mTapExcludeRegion.isEmpty(); } - @Override - public boolean isInputMethodTarget() { + boolean isInputMethodTarget() { return getDisplayContent().mInputMethodTarget == this; } long getFrameNumber() { // Return the frame number in which changes requested in this layout will be rendered or // -1 if we do not expect the frame to be rendered. - return getFrameLw().isEmpty() ? -1 : mFrameNumber; + return getFrame().isEmpty() ? -1 : mFrameNumber; } void setFrameNumber(long frameNumber) { @@ -5691,8 +5690,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mWindowFrames.mVisibleInsets; } - @Override - public WindowFrames getWindowFrames() { + /** Returns the {@link WindowFrames} associated with this {@link WindowState}. */ + WindowFrames getWindowFrames() { return mWindowFrames; } @@ -5800,7 +5799,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // won't exactly match the final freeform window frame (e.g. when overlapping with // the status bar). In that case we need to use the final frame. if (inFreeformWindowingMode()) { - outFrame.set(getFrameLw()); + outFrame.set(getFrame()); } else if (isLetterboxedAppWindow() || mToken.isFixedRotationTransforming()) { // 1. The letterbox surfaces should be animated with the owner activity, so use task // bounds to include them. diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 6f483428eaec..1bd712c3638b 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -526,13 +526,13 @@ class WindowStateAnimator { if (DEBUG) { Slog.v(TAG, "Got surface: " + mSurfaceController - + ", set left=" + w.getFrameLw().left + " top=" + w.getFrameLw().top); + + ", set left=" + w.getFrame().left + " top=" + w.getFrame().top); } if (SHOW_LIGHT_TRANSACTIONS) { Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked"); WindowManagerService.logSurface(w, "CREATE pos=(" - + w.getFrameLw().left + "," + w.getFrameLw().top + ") (" + + w.getFrame().left + "," + w.getFrame().top + ") (" + width + "x" + height + ")" + " HIDE", false); } @@ -896,7 +896,7 @@ class WindowStateAnimator { // There is no need to wait for an animation change if our window is gone for layout // already as we'll never be visible. - if (w.getOrientationChanging() && w.isGoneForLayoutLw()) { + if (w.getOrientationChanging() && w.isGoneForLayout()) { ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation change skips hidden %s", w); w.setOrientationChanging(false); } @@ -920,7 +920,7 @@ class WindowStateAnimator { // really hidden (gone for layout), there is no point in still waiting for it. // Note that this does introduce a potential glitch if the window becomes unhidden // before it has drawn for the new orientation. - if (w.getOrientationChanging() && w.isGoneForLayoutLw()) { + if (w.getOrientationChanging() && w.isGoneForLayout()) { w.setOrientationChanging(false); ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation change skips hidden %s", w); @@ -998,7 +998,7 @@ class WindowStateAnimator { } if (w.getOrientationChanging()) { - if (!w.isDrawnLw()) { + if (!w.isDrawn()) { mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE; mAnimator.mLastWindowFreezeSource = w; ProtoLog.v(WM_DEBUG_ORIENTATION, diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp index b3f3a5e1ff72..9aca84849fc6 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -40,14 +40,12 @@ namespace aidl = android::hardware::vibrator; namespace android { static JavaVM* sJvm = nullptr; - static jmethodID sMethodIdOnComplete; - static struct { jfieldID id; jfieldID scale; jfieldID delay; -} gPrimitiveClassInfo; +} sPrimitiveClassInfo; static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) == static_cast<uint8_t>(aidl::EffectStrength::LIGHT)); @@ -77,100 +75,117 @@ static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) == static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) == static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK)); -static inline void callVibrationOnComplete(jobject vibration) { - if (vibration == nullptr) { - return; +class NativeVibratorService { +public: + NativeVibratorService(JNIEnv* env, jobject callbackListener) + : mController(std::make_unique<vibrator::HalController>()), + mCallbackListener(env->NewGlobalRef(callbackListener)) { + LOG_ALWAYS_FATAL_IF(mCallbackListener == nullptr, + "Unable to create global reference to vibration callback handler"); } - auto jniEnv = GetOrAttachJNIEnvironment(sJvm); - jniEnv->CallVoidMethod(vibration, sMethodIdOnComplete); - jniEnv->DeleteGlobalRef(vibration); -} + + ~NativeVibratorService() { + auto jniEnv = GetOrAttachJNIEnvironment(sJvm); + jniEnv->DeleteGlobalRef(mCallbackListener); + } + + vibrator::HalController* controller() const { return mController.get(); } + + std::function<void()> createCallback(jlong vibrationId) { + return [vibrationId, this]() { + auto jniEnv = GetOrAttachJNIEnvironment(sJvm); + jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, vibrationId); + }; + } + +private: + const std::unique_ptr<vibrator::HalController> mController; + const jobject mCallbackListener; +}; static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) { aidl::CompositeEffect effect; effect.primitive = static_cast<aidl::CompositePrimitive>( - env->GetIntField(primitive, gPrimitiveClassInfo.id)); - effect.scale = static_cast<float>(env->GetFloatField(primitive, gPrimitiveClassInfo.scale)); - effect.delayMs = static_cast<int32_t>(env->GetIntField(primitive, gPrimitiveClassInfo.delay)); + env->GetIntField(primitive, sPrimitiveClassInfo.id)); + effect.scale = static_cast<float>(env->GetFloatField(primitive, sPrimitiveClassInfo.scale)); + effect.delayMs = static_cast<int32_t>(env->GetIntField(primitive, sPrimitiveClassInfo.delay)); return effect; } -static void destroyVibratorController(void* rawVibratorController) { - vibrator::HalController* vibratorController = - reinterpret_cast<vibrator::HalController*>(rawVibratorController); - if (vibratorController) { - delete vibratorController; +static void destroyNativeService(void* servicePtr) { + NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); + if (service) { + delete service; } } -static jlong vibratorInit(JNIEnv* /* env */, jclass /* clazz */) { - std::unique_ptr<vibrator::HalController> controller = - std::make_unique<vibrator::HalController>(); - controller->init(); - return reinterpret_cast<jlong>(controller.release()); +static jlong vibratorInit(JNIEnv* env, jclass /* clazz */, jobject callbackListener) { + std::unique_ptr<NativeVibratorService> service = + std::make_unique<NativeVibratorService>(env, callbackListener); + service->controller()->init(); + return reinterpret_cast<jlong>(service.release()); } static jlong vibratorGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) { - return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyVibratorController)); + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeService)); } -static jboolean vibratorExists(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { - vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); - if (controller == nullptr) { - ALOGE("vibratorExists failed because controller was not initialized"); +static jboolean vibratorExists(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { + NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); + if (service == nullptr) { + ALOGE("vibratorExists failed because native service was not initialized"); return JNI_FALSE; } - return controller->ping().isOk() ? JNI_TRUE : JNI_FALSE; + return service->controller()->ping().isOk() ? JNI_TRUE : JNI_FALSE; } -static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, jlong timeoutMs, - jobject vibration) { - vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); - if (controller == nullptr) { - ALOGE("vibratorOn failed because controller was not initialized"); +static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong timeoutMs, + jlong vibrationId) { + NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); + if (service == nullptr) { + ALOGE("vibratorOn failed because native service was not initialized"); return; } - jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration); - auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); }; - controller->on(std::chrono::milliseconds(timeoutMs), callback); + auto callback = service->createCallback(vibrationId); + service->controller()->on(std::chrono::milliseconds(timeoutMs), callback); } -static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { - vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); - if (controller == nullptr) { - ALOGE("vibratorOff failed because controller was not initialized"); +static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { + NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); + if (service == nullptr) { + ALOGE("vibratorOff failed because native service was not initialized"); return; } - controller->off(); + service->controller()->off(); } -static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, +static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jint amplitude) { - vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); - if (controller == nullptr) { - ALOGE("vibratorSetAmplitude failed because controller was not initialized"); + NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); + if (service == nullptr) { + ALOGE("vibratorSetAmplitude failed because native service was not initialized"); return; } - controller->setAmplitude(static_cast<int32_t>(amplitude)); + service->controller()->setAmplitude(static_cast<int32_t>(amplitude)); } -static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, +static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jboolean enabled) { - vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); - if (controller == nullptr) { - ALOGE("vibratorSetExternalControl failed because controller was not initialized"); + NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); + if (service == nullptr) { + ALOGE("vibratorSetExternalControl failed because native service was not initialized"); return; } - controller->setExternalControl(enabled); + service->controller()->setExternalControl(enabled); } -static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { - vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); - if (controller == nullptr) { - ALOGE("vibratorGetSupportedEffects failed because controller was not initialized"); +static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { + NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); + if (service == nullptr) { + ALOGE("vibratorGetSupportedEffects failed because native service was not initialized"); return nullptr; } - auto result = controller->getSupportedEffects(); + auto result = service->controller()->getSupportedEffects(); if (!result.isOk()) { return nullptr; } @@ -181,14 +196,13 @@ static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jl return effects; } -static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */, - jlong controllerPtr) { - vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); - if (controller == nullptr) { - ALOGE("vibratorGetSupportedPrimitives failed because controller was not initialized"); +static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { + NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); + if (service == nullptr) { + ALOGE("vibratorGetSupportedPrimitives failed because native service was not initialized"); return nullptr; } - auto result = controller->getSupportedPrimitives(); + auto result = service->controller()->getSupportedPrimitives(); if (!result.isOk()) { return nullptr; } @@ -199,26 +213,25 @@ static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */, return primitives; } -static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, - jlong effect, jlong strength, jobject vibration) { - vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); - if (controller == nullptr) { - ALOGE("vibratorPerformEffect failed because controller was not initialized"); +static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong effect, + jlong strength, jlong vibrationId) { + NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); + if (service == nullptr) { + ALOGE("vibratorPerformEffect failed because native service was not initialized"); return -1; } aidl::Effect effectType = static_cast<aidl::Effect>(effect); aidl::EffectStrength effectStrength = static_cast<aidl::EffectStrength>(strength); - jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration); - auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); }; - auto result = controller->performEffect(effectType, effectStrength, callback); + auto callback = service->createCallback(vibrationId); + auto result = service->controller()->performEffect(effectType, effectStrength, callback); return result.isOk() ? result.value().count() : -1; } -static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, - jobjectArray composition, jobject vibration) { - vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); - if (controller == nullptr) { - ALOGE("vibratorPerformComposedEffect failed because controller was not initialized"); +static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong servicePtr, + jobjectArray composition, jlong vibrationId) { + NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); + if (service == nullptr) { + ALOGE("vibratorPerformComposedEffect failed because native service was not initialized"); return; } size_t size = env->GetArrayLength(composition); @@ -227,54 +240,52 @@ static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong jobject element = env->GetObjectArrayElement(composition, i); effects.push_back(effectFromJavaPrimitive(env, element)); } - jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration); - auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); }; - controller->performComposedEffect(effects, callback); + auto callback = service->createCallback(vibrationId); + service->controller()->performComposedEffect(effects, callback); } -static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { - vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); - if (controller == nullptr) { - ALOGE("vibratorGetCapabilities failed because controller was not initialized"); +static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { + NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); + if (service == nullptr) { + ALOGE("vibratorGetCapabilities failed because native service was not initialized"); return 0; } - auto result = controller->getCapabilities(); + auto result = service->controller()->getCapabilities(); return result.isOk() ? static_cast<jlong>(result.value()) : 0; } -static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, jlong id, +static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong id, jlong effect, jlong strength) { - vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); - if (controller == nullptr) { - ALOGE("vibratorAlwaysOnEnable failed because controller was not initialized"); + NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); + if (service == nullptr) { + ALOGE("vibratorAlwaysOnEnable failed because native service was not initialized"); return; } - controller->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect), - static_cast<aidl::EffectStrength>(strength)); + service->controller()->alwaysOnEnable(static_cast<int32_t>(id), + static_cast<aidl::Effect>(effect), + static_cast<aidl::EffectStrength>(strength)); } -static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, - jlong id) { - vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); - if (controller == nullptr) { - ALOGE("vibratorAlwaysOnDisable failed because controller was not initialized"); +static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong id) { + NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); + if (service == nullptr) { + ALOGE("vibratorAlwaysOnDisable failed because native service was not initialized"); return; } - controller->alwaysOnDisable(static_cast<int32_t>(id)); + service->controller()->alwaysOnDisable(static_cast<int32_t>(id)); } static const JNINativeMethod method_table[] = { - {"vibratorInit", "()J", (void*)vibratorInit}, + {"vibratorInit", "(Lcom/android/server/VibratorService$OnCompleteListener;)J", + (void*)vibratorInit}, {"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer}, {"vibratorExists", "(J)Z", (void*)vibratorExists}, - {"vibratorOn", "(JJLcom/android/server/VibratorService$Vibration;)V", (void*)vibratorOn}, + {"vibratorOn", "(JJJ)V", (void*)vibratorOn}, {"vibratorOff", "(J)V", (void*)vibratorOff}, {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude}, - {"vibratorPerformEffect", "(JJJLcom/android/server/VibratorService$Vibration;)J", - (void*)vibratorPerformEffect}, + {"vibratorPerformEffect", "(JJJJ)J", (void*)vibratorPerformEffect}, {"vibratorPerformComposedEffect", - "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/" - "VibratorService$Vibration;)V", + "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)V", (void*)vibratorPerformComposedEffect}, {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects}, {"vibratorGetSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives}, @@ -284,18 +295,17 @@ static const JNINativeMethod method_table[] = { {"vibratorAlwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable}, }; -int register_android_server_VibratorService(JavaVM* vm, JNIEnv* env) { - sJvm = vm; - sMethodIdOnComplete = - GetMethodIDOrDie(env, - FindClassOrDie(env, "com/android/server/VibratorService$Vibration"), - "onComplete", "()V"); +int register_android_server_VibratorService(JavaVM* jvm, JNIEnv* env) { + sJvm = jvm; + jclass listenerClass = + FindClassOrDie(env, "com/android/server/VibratorService$OnCompleteListener"); + sMethodIdOnComplete = GetMethodIDOrDie(env, listenerClass, "onComplete", "(J)V"); jclass primitiveClass = FindClassOrDie(env, "android/os/VibrationEffect$Composition$PrimitiveEffect"); - gPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "id", "I"); - gPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F"); - gPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I"); + sPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "id", "I"); + sPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F"); + sPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I"); return jniRegisterNativeMethods(env, "com/android/server/VibratorService", method_table, NELEM(method_table)); diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp index 0277f16d5e54..46e6f912edb0 100644 --- a/services/core/jni/com_android_server_security_VerityUtils.cpp +++ b/services/core/jni/com_android_server_security_VerityUtils.cpp @@ -33,6 +33,8 @@ #include <android-base/unique_fd.h> +#include <type_traits> + namespace android { namespace { @@ -53,7 +55,7 @@ int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath, jbyteArra fsverity_enable_arg arg = {}; arg.version = 1; - arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; + arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; // hardcoded in measureFsverity below arg.block_size = 4096; arg.salt_size = 0; arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr); @@ -85,9 +87,41 @@ int statxForFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath) { return (out.stx_attributes & STATX_ATTR_VERITY) != 0; } +int measureFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath, jbyteArray digest) { + static constexpr auto kDigestSha256 = 32; + using Storage = std::aligned_storage_t<sizeof(fsverity_digest) + kDigestSha256>; + + Storage bytes; + fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes); + data->digest_size = kDigestSha256; // the only input/output parameter + + ScopedUtfChars path(env, filePath); + ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC)); + if (rfd.get() < 0) { + return rfd.get(); + } + if (auto err = ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data); err < 0) { + return err; + } + + if (data->digest_algorithm != FS_VERITY_HASH_ALG_SHA256) { + return -EINVAL; + } + + if (digest != nullptr && data->digest_size > 0) { + auto digestSize = env->GetArrayLength(digest); + if (data->digest_size > digestSize) { + return -E2BIG; + } + env->SetByteArrayRegion(digest, 0, data->digest_size, (const jbyte *)data->digest); + } + + return 0; +} const JNINativeMethod sMethods[] = { {"enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity}, {"statxForFsverityNative", "(Ljava/lang/String;)I", (void *)statxForFsverity}, + {"measureFsverityNative", "(Ljava/lang/String;[B)I", (void *)measureFsverity}, }; } // namespace diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 22e309cdc2b4..277218d4e543 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -12052,14 +12052,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public boolean isSystemOnlyUser(ComponentName admin) { - Objects.requireNonNull(admin, "ComponentName is null"); - final CallerIdentity identity = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(identity)); - return UserManager.isSplitSystemUser() && identity.getUserId() == UserHandle.USER_SYSTEM; - } - - @Override public void reboot(ComponentName admin) { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity identity = getCallerIdentity(admin); diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index ba6ae9262aea..9836262ec2b0 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -1706,8 +1706,8 @@ float IncrementalService::getLoadingProgressFromPath(const IncFsMount& ifs, } if (totalBlocks == 0) { - LOG(ERROR) << "getLoadingProgress failed to get total num of blocks"; - return -EINVAL; + // No file in the storage or files are empty; regarded as fully loaded + return 1; } return (float)filledBlocks / (float)totalBlocks; } diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index 44cef49a716c..d1000e56e5ee 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -1069,7 +1069,7 @@ TEST_F(IncrementalServiceTest, testMakeDirectories) { ASSERT_EQ(res, 0); } -TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithNoFile) { +TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithNoFile) { mIncFs->countFilledBlocksSuccess(); mFs->hasNoFile(); @@ -1077,7 +1077,7 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithNoFile) { int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, {}, {}, {}); - ASSERT_EQ(-EINVAL, mIncrementalService->getLoadingProgress(storageId)); + ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId)); } TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) { @@ -1092,7 +1092,7 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) { ASSERT_EQ(-1, mIncrementalService->getLoadingProgress(storageId)); } -TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithEmptyRanges) { +TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) { mIncFs->countFilledBlocksEmpty(); mFs->hasFiles(); @@ -1101,7 +1101,7 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithEmptyRanges) { IncrementalService::CreateOptions::CreateNew, {}, {}, {}); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3); - ASSERT_EQ(-EINVAL, mIncrementalService->getLoadingProgress(storageId)); + ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId)); } TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) { diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java index fdcadf3e3088..d6894cf2a4e8 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java @@ -33,6 +33,7 @@ import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; import static com.android.server.location.LocationPermissions.PERMISSION_COARSE; import static com.android.server.location.LocationPermissions.PERMISSION_FINE; import static com.android.server.location.LocationUtils.createLocation; +import static com.android.server.location.listeners.RemoteListenerRegistration.IN_PROCESS_EXECUTOR; import static com.google.common.truth.Truth.assertThat; @@ -85,7 +86,6 @@ import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; import com.android.server.FgThread; import com.android.server.LocalServices; -import com.android.server.location.listeners.ListenerRegistration; import com.android.server.location.util.FakeUserInfoHelper; import com.android.server.location.util.TestInjector; @@ -484,7 +484,7 @@ public class LocationProviderManagerTest { PERMISSION_FINE, listener); CountDownLatch blocker = new CountDownLatch(1); - ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> { + IN_PROCESS_EXECUTOR.execute(() -> { try { blocker.await(); } catch (InterruptedException e) { @@ -622,7 +622,7 @@ public class LocationProviderManagerTest { PERMISSION_FINE, listener); CountDownLatch blocker = new CountDownLatch(1); - ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> { + IN_PROCESS_EXECUTOR.execute(() -> { try { blocker.await(); } catch (InterruptedException e) { diff --git a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java index 1ef12555a83a..69a9f4415fe7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java @@ -16,6 +16,8 @@ package com.android.server.location.listeners; +import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -27,8 +29,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.testng.Assert.assertThrows; -import android.location.util.identity.CallerIdentity; -import android.os.Process; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; @@ -324,10 +324,8 @@ public class ListenerMultiplexerTest { boolean mActive = true; protected TestListenerRegistration(Integer integer, - Consumer<TestListenerRegistration> consumer, - boolean outOfProcess) { - super(integer, CallerIdentity.forTest(Process.myUid(), - Process.myPid() + (outOfProcess ? 1 : 0), "test", "test"), consumer); + Consumer<TestListenerRegistration> consumer) { + super(DIRECT_EXECUTOR, integer, consumer); } } @@ -345,7 +343,7 @@ public class ListenerMultiplexerTest { } public void addListener(Integer request, Consumer<TestListenerRegistration> consumer) { - addRegistration(consumer, new TestListenerRegistration(request, consumer, true)); + addRegistration(consumer, new TestListenerRegistration(request, consumer)); } public void removeListener(Consumer<TestListenerRegistration> consumer) { diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java index b7a36f2eaed2..8d4f2aa69d68 100644 --- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java @@ -20,12 +20,13 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.AdditionalMatchers.gt; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.intThat; -import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; @@ -167,7 +168,7 @@ public class VibratorServiceTest { @Test public void createService_initializesNativeService() { createService(); - verify(mNativeWrapperMock).vibratorInit(); + verify(mNativeWrapperMock).vibratorInit(notNull()); verify(mNativeWrapperMock).vibratorOff(); } @@ -294,7 +295,7 @@ public class VibratorServiceTest { assertTrue(service.isVibrating()); verify(mNativeWrapperMock).vibratorOff(); - verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class)); + verify(mNativeWrapperMock).vibratorOn(eq(100L), gt(0L)); verify(mNativeWrapperMock).vibratorSetAmplitude(eq(128)); } @@ -307,7 +308,7 @@ public class VibratorServiceTest { assertTrue(service.isVibrating()); verify(mNativeWrapperMock).vibratorOff(); - verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class)); + verify(mNativeWrapperMock).vibratorOn(eq(100L), gt(0L)); verify(mNativeWrapperMock, never()).vibratorSetAmplitude(anyInt()); } @@ -321,10 +322,8 @@ public class VibratorServiceTest { vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); verify(mNativeWrapperMock).vibratorOff(); - verify(mNativeWrapperMock).vibratorPerformEffect( - eq((long) VibrationEffect.EFFECT_CLICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), - any(VibratorService.Vibration.class)); + verify(mNativeWrapperMock).vibratorPerformEffect(eq((long) VibrationEffect.EFFECT_CLICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), gt(0L)); } @Test @@ -343,7 +342,7 @@ public class VibratorServiceTest { verify(mNativeWrapperMock).vibratorOff(); verify(mNativeWrapperMock).vibratorPerformComposedEffect( - primitivesCaptor.capture(), any(VibratorService.Vibration.class)); + primitivesCaptor.capture(), gt(0L)); // Check all primitive effect fields are passed down to the HAL. assertEquals(1, primitivesCaptor.getValue().length); @@ -368,7 +367,7 @@ public class VibratorServiceTest { // Wait for VibrateThread to turn vibrator ON with total timing and no callback. Thread.sleep(5); - verify(mNativeWrapperMock).vibratorOn(eq(30L), isNull()); + verify(mNativeWrapperMock).vibratorOn(eq(30L), eq(0L)); // First amplitude set right away. verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100)); @@ -384,11 +383,11 @@ public class VibratorServiceTest { @Test public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() { + VibratorService service = createService(); doAnswer(invocation -> { - ((VibratorService.Vibration) invocation.getArgument(1)).onComplete(); + service.onVibrationComplete(invocation.getArgument(1)); return null; - }).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class)); - VibratorService service = createService(); + }).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong()); Mockito.clearInvocations(mNativeWrapperMock); vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); @@ -396,7 +395,7 @@ public class VibratorServiceTest { InOrder inOrderVerifier = inOrder(mNativeWrapperMock); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(100L), - any(VibratorService.Vibration.class)); + gt(0L)); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); } @@ -404,12 +403,11 @@ public class VibratorServiceTest { public void vibrate_withPrebakedAndNativeCallbackTriggered_finishesVibration() { when(mNativeWrapperMock.vibratorGetSupportedEffects()) .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); + VibratorService service = createService(); doAnswer(invocation -> { - ((VibratorService.Vibration) invocation.getArgument(2)).onComplete(); + service.onVibrationComplete(invocation.getArgument(2)); return 10_000L; // 10s - }).when(mNativeWrapperMock).vibratorPerformEffect( - anyLong(), anyLong(), any(VibratorService.Vibration.class)); - VibratorService service = createService(); + }).when(mNativeWrapperMock).vibratorPerformEffect(anyLong(), anyLong(), anyLong()); Mockito.clearInvocations(mNativeWrapperMock); vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); @@ -419,7 +417,7 @@ public class VibratorServiceTest { inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), - any(VibratorService.Vibration.class)); + gt(0L)); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); } @@ -436,44 +434,19 @@ public class VibratorServiceTest { Thread.sleep(15); InOrder inOrderVerifier = inOrder(mNativeWrapperMock); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(3L), isNull()); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), isNull()); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(3L), eq(0L)); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), eq(0L)); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); } @Test public void vibrate_withComposedAndNativeCallbackTriggered_finishesVibration() { mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); - doAnswer(invocation -> { - ((VibratorService.Vibration) invocation.getArgument(1)).onComplete(); - return null; - }).when(mNativeWrapperMock).vibratorPerformComposedEffect( - any(), any(VibratorService.Vibration.class)); VibratorService service = createService(); - Mockito.clearInvocations(mNativeWrapperMock); - - VibrationEffect effect = VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10) - .compose(); - vibrate(service, effect); - - InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); - inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect( - any(VibrationEffect.Composition.PrimitiveEffect[].class), - any(VibratorService.Vibration.class)); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); - } - - @Test - public void vibrate_whenBinderDies_cancelsVibration() { - mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); doAnswer(invocation -> { - ((VibratorService.Vibration) invocation.getArgument(1)).binderDied(); + service.onVibrationComplete(invocation.getArgument(1)); return null; - }).when(mNativeWrapperMock).vibratorPerformComposedEffect( - any(), any(VibratorService.Vibration.class)); - VibratorService service = createService(); + }).when(mNativeWrapperMock).vibratorPerformComposedEffect(any(), anyLong()); Mockito.clearInvocations(mNativeWrapperMock); VibrationEffect effect = VibrationEffect.startComposition() @@ -484,8 +457,7 @@ public class VibratorServiceTest { InOrder inOrderVerifier = inOrder(mNativeWrapperMock); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect( - any(VibrationEffect.Composition.PrimitiveEffect[].class), - any(VibratorService.Vibration.class)); + any(VibrationEffect.Composition.PrimitiveEffect[].class), gt(0L)); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); } @@ -513,12 +485,11 @@ public class VibratorServiceTest { @Test public void registerVibratorStateListener_callbacksAreTriggered() throws Exception { + VibratorService service = createService(); doAnswer(invocation -> { - ((VibratorService.Vibration) invocation.getArgument(1)).onComplete(); + service.onVibrationComplete(invocation.getArgument(1)); return null; - }).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class)); - VibratorService service = createService(); - + }).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong()); service.registerVibratorStateListener(mVibratorStateListenerMock); verify(mVibratorStateListenerMock).onVibrating(false); Mockito.clearInvocations(mVibratorStateListenerMock); @@ -569,15 +540,15 @@ public class VibratorServiceTest { verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_CLICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any()); + eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), anyLong()); verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_TICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), any()); + eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), anyLong()); verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_DOUBLE_CLICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), any()); + eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), anyLong()); verify(mNativeWrapperMock, never()).vibratorPerformEffect( - eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), any()); + eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), anyLong()); } @Test @@ -644,7 +615,7 @@ public class VibratorServiceTest { // Ringtone vibration is off, so only the other 3 are propagated to native. verify(mNativeWrapperMock, times(3)).vibratorPerformComposedEffect( - primitivesCaptor.capture(), any()); + primitivesCaptor.capture(), anyLong()); List<VibrationEffect.Composition.PrimitiveEffect[]> values = primitivesCaptor.getAllValues(); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java index 763654d24047..dda81ffded4f 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java @@ -187,7 +187,7 @@ public class TouchExplorerTest { moveEachPointers(mLastEvent, p(10, 10), p(10, 10)); send(mLastEvent); goToStateClearFrom(STATE_DRAGGING_2FINGERS); - assertCapturedEvents(ACTION_DOWN, ACTION_MOVE, ACTION_MOVE, ACTION_UP); + assertCapturedEvents(ACTION_DOWN, ACTION_MOVE, ACTION_MOVE, ACTION_MOVE, ACTION_UP); } @Test @@ -288,7 +288,7 @@ public class TouchExplorerTest { assertState(STATE_DRAGGING); goToStateClearFrom(STATE_DRAGGING_2FINGERS); assertState(STATE_CLEAR); - assertCapturedEvents(ACTION_DOWN, ACTION_UP); + assertCapturedEvents(ACTION_DOWN, ACTION_MOVE, ACTION_UP); assertCapturedEventsNoHistory(); } @@ -301,6 +301,7 @@ public class TouchExplorerTest { assertState(STATE_CLEAR); assertCapturedEvents( /* goto dragging state */ ACTION_DOWN, + ACTION_MOVE, /* leave dragging state */ ACTION_UP, ACTION_DOWN, ACTION_POINTER_DOWN, diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java index bec9f26672f4..a10e0ba5020c 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java @@ -81,7 +81,7 @@ public class WindowMagnificationGestureHandlerTest { @After public void tearDown() { - mWindowMagnificationManager.disableWindowMagnifier(DISPLAY_0, true); + mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, true); } @Test @@ -225,7 +225,7 @@ public class WindowMagnificationGestureHandlerTest { } break; case STATE_SHOW_MAGNIFIER: { - mWindowMagnificationManager.disableWindowMagnifier(DISPLAY_0, false); + mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, false); } break; case STATE_TWO_FINGERS_DOWN: { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java index 70e6a340816a..e067b7eca755 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java @@ -68,8 +68,10 @@ public class WindowMagnificationManagerTest { private static final int CURRENT_USER_ID = UserHandle.USER_CURRENT; private MockWindowMagnificationConnection mMockConnection; - @Mock private Context mContext; - @Mock private StatusBarManagerInternal mMockStatusBarManagerInternal; + @Mock + private Context mContext; + @Mock + private StatusBarManagerInternal mMockStatusBarManagerInternal; private MockContentResolver mResolver; private WindowMagnificationManager mWindowMagnificationManager; @@ -84,7 +86,7 @@ public class WindowMagnificationManagerTest { when(mContext.getContentResolver()).thenReturn(mResolver); doAnswer((InvocationOnMock invocation) -> { - final boolean connect = (Boolean) invocation.getArguments()[0]; + final boolean connect = (Boolean) invocation.getArguments()[0]; mWindowMagnificationManager.setConnection( connect ? mMockConnection.getConnection() : null); return null; @@ -161,7 +163,7 @@ public class WindowMagnificationManagerTest { public void enable_TestDisplay_enableWindowMagnification() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2f, 200f, 300f); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f); verify(mMockConnection.getConnection()).enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f); @@ -170,9 +172,9 @@ public class WindowMagnificationManagerTest { @Test public void disable_testDisplay_disableWindowMagnification() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); - mWindowMagnificationManager.disableWindowMagnifier(TEST_DISPLAY, false); + mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false); verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY); } @@ -183,7 +185,7 @@ public class WindowMagnificationManagerTest { assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN); assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); } @@ -198,7 +200,7 @@ public class WindowMagnificationManagerTest { @Test public void persistScale_setValue_expectedValueInProvider() { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2.0f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN); mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f); mWindowMagnificationManager.persistScale(TEST_DISPLAY); @@ -211,7 +213,7 @@ public class WindowMagnificationManagerTest { @Test public void scaleSetterGetter_enabledOnTestDisplay_expectedValue() { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2.0f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN); mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f); @@ -221,7 +223,7 @@ public class WindowMagnificationManagerTest { @Test public void scaleSetterGetter_scaleIsOutOfRang_getNormalizeValue() { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2.5f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN); mWindowMagnificationManager.setScale(TEST_DISPLAY, 10.0f); @@ -232,9 +234,9 @@ public class WindowMagnificationManagerTest { @Test public void moveWindowMagnifier() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN); - mWindowMagnificationManager.moveWindowMagnifier(TEST_DISPLAY, 200, 300); + mWindowMagnificationManager.moveWindowMagnification(TEST_DISPLAY, 200, 300); verify(mMockConnection.getConnection()).moveWindowMagnifier(TEST_DISPLAY, 200, 300); } @@ -254,7 +256,7 @@ public class WindowMagnificationManagerTest { @Test public void pointersInWindow_returnCorrectValue() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3.0f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN); mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(TEST_DISPLAY, new Rect(0, 0, 500, 500)); PointF[] pointersLocation = new PointF[2]; @@ -268,7 +270,7 @@ public class WindowMagnificationManagerTest { @Test public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); mMockConnection.getDeathRecipient().binderDied(); @@ -280,7 +282,7 @@ public class WindowMagnificationManagerTest { requestConnectionToNull_disableAllMagnifiersAndRequestWindowMagnificationConnection() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); assertTrue(mWindowMagnificationManager.requestConnection(false)); @@ -306,21 +308,24 @@ public class WindowMagnificationManagerTest { @Test public void requestConnection_registerAndUnregisterBroadcastReceiver() { assertTrue(mWindowMagnificationManager.requestConnection(true)); - verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); + verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); assertTrue(mWindowMagnificationManager.requestConnection(false)); verify(mContext).unregisterReceiver(any(BroadcastReceiver.class)); } @Test - public void onReceiveScreenOff_removeMagnificationButtonAndDisableWindowMagnification() + public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification() throws RemoteException { mWindowMagnificationManager.requestConnection(true); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN); + mWindowMagnificationManager.mScreenStateReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY); verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY); + assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); } private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index ef2365e6da3e..3f324a279270 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -15,6 +15,7 @@ */ package com.android.server.hdmi; +import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM; import static com.android.server.hdmi.Constants.ADDR_BROADCAST; import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; @@ -693,6 +694,82 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test + public void sendVolumeKeyEvent_toTv_activeSource() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setSystemAudioActivated(false); + mHdmiControlService.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress); + + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false); + + HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed( + mPlaybackLogicalAddress, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP); + HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased( + mPlaybackLogicalAddress, ADDR_TV); + mTestLooper.dispatchAll(); + + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue(); + assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released); + } + + @Test + public void sendVolumeKeyEvent_toAudio_activeSource() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setSystemAudioActivated(true); + mHdmiControlService.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress); + + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false); + + HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed( + mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP); + HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased( + mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM); + mTestLooper.dispatchAll(); + + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue(); + assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released); + } + + @Test + public void sendVolumeKeyEvent_toTv_inactiveSource() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setSystemAudioActivated(false); + mHdmiControlService.setActiveSource(ADDR_TV, 0x0000); + + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false); + + HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed( + mPlaybackLogicalAddress, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP); + HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased( + mPlaybackLogicalAddress, ADDR_TV); + mTestLooper.dispatchAll(); + + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse(); + assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released); + } + + @Test + public void sendVolumeKeyEvent_toAudio_inactiveSource() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setSystemAudioActivated(true); + mHdmiControlService.setActiveSource(ADDR_TV, 0x0000); + + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false); + + HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed( + mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP); + HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased( + mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM); + mTestLooper.dispatchAll(); + + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse(); + assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released); + } + + @Test public void handleSetStreamPath_broadcastsActiveSource() { HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, mPlaybackPhysicalAddress); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java index 53c4d6faf0b9..f17173f61b69 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java @@ -62,6 +62,32 @@ public class HdmiCecMessageBuilderTest { assertThat(message).isEqualTo(buildMessage("5F:81:21:00")); } + @Test + public void buildSetOsdName_short() { + String deviceName = "abc"; + HdmiCecMessage message = HdmiCecMessageBuilder.buildSetOsdNameCommand(ADDR_PLAYBACK_1, + ADDR_TV, deviceName); + assertThat(message).isEqualTo(buildMessage("40:47:61:62:63")); + } + + @Test + public void buildSetOsdName_maximumLength() { + String deviceName = "abcdefghijklmn"; + HdmiCecMessage message = HdmiCecMessageBuilder.buildSetOsdNameCommand(ADDR_PLAYBACK_1, + ADDR_TV, deviceName); + assertThat(message).isEqualTo( + buildMessage("40:47:61:62:63:64:65:66:67:68:69:6A:6B:6C:6D:6E")); + } + + @Test + public void buildSetOsdName_tooLong() { + String deviceName = "abcdefghijklmnop"; + HdmiCecMessage message = HdmiCecMessageBuilder.buildSetOsdNameCommand(ADDR_PLAYBACK_1, + ADDR_TV, deviceName); + assertThat(message).isEqualTo( + buildMessage("40:47:61:62:63:64:65:66:67:68:69:6A:6B:6C:6D:6E")); + } + /** * Build a CEC message from a hex byte string with bytes separated by {@code :}. * diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java new file mode 100644 index 000000000000..d7ed96fd5833 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java @@ -0,0 +1,182 @@ +/* + * 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.timezonedetector; + +import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.app.timezonedetector.TimeZoneCapabilities; + +import org.junit.Test; + +/** + * Tests for {@link ConfigurationInternal} and the {@link TimeZoneCapabilities} and + * {@link android.app.timezonedetector.TimeZoneConfiguration} that can be generated from it. + */ +public class ConfigurationInternalTest { + + private static final int ARBITRARY_USER_ID = 99999; + + /** + * Tests when {@link ConfigurationInternal#isUserConfigAllowed()} and + * {@link ConfigurationInternal#isAutoDetectionSupported()} are both true. + */ + @Test + public void test_unrestricted() { + ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) + .setUserConfigAllowed(true) + .setAutoDetectionSupported(true) + .setAutoDetectionEnabled(true) + .setLocationEnabled(true) + .setGeoDetectionEnabled(true) + .build(); + { + ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig) + .setAutoDetectionEnabled(true) + .build(); + assertTrue(autoOnConfig.getAutoDetectionEnabledSetting()); + assertTrue(autoOnConfig.getGeoDetectionEnabledSetting()); + assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior()); + assertTrue(autoOnConfig.getGeoDetectionEnabledBehavior()); + + TimeZoneCapabilities capabilities = autoOnConfig.createCapabilities(); + assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled()); + assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled()); + assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSuggestManualTimeZone()); + assertEquals(autoOnConfig.asConfiguration(), capabilities.getConfiguration()); + assertTrue(capabilities.getConfiguration().isAutoDetectionEnabled()); + assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled()); + } + + { + ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig) + .setAutoDetectionEnabled(false) + .build(); + assertFalse(autoOffConfig.getAutoDetectionEnabledSetting()); + assertTrue(autoOffConfig.getGeoDetectionEnabledSetting()); + assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior()); + assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior()); + + TimeZoneCapabilities capabilities = autoOffConfig.createCapabilities(); + assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled()); + assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled()); + assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone()); + assertEquals(autoOffConfig.asConfiguration(), capabilities.getConfiguration()); + assertFalse(capabilities.getConfiguration().isAutoDetectionEnabled()); + assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled()); + } + } + + /** Tests when {@link ConfigurationInternal#isUserConfigAllowed()} is false */ + @Test + public void test_restricted() { + ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) + .setUserConfigAllowed(false) + .setAutoDetectionSupported(true) + .setAutoDetectionEnabled(true) + .setLocationEnabled(true) + .setGeoDetectionEnabled(true) + .build(); + { + ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig) + .setAutoDetectionEnabled(true) + .build(); + assertTrue(autoOnConfig.getAutoDetectionEnabledSetting()); + assertTrue(autoOnConfig.getGeoDetectionEnabledSetting()); + assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior()); + assertTrue(autoOnConfig.getGeoDetectionEnabledBehavior()); + + TimeZoneCapabilities capabilities = autoOnConfig.createCapabilities(); + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled()); + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled()); + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone()); + assertEquals(autoOnConfig.asConfiguration(), capabilities.getConfiguration()); + assertTrue(capabilities.getConfiguration().isAutoDetectionEnabled()); + assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled()); + } + + { + ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig) + .setAutoDetectionEnabled(false) + .build(); + assertFalse(autoOffConfig.getAutoDetectionEnabledSetting()); + assertTrue(autoOffConfig.getGeoDetectionEnabledSetting()); + assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior()); + assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior()); + + TimeZoneCapabilities capabilities = autoOffConfig.createCapabilities(); + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled()); + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled()); + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone()); + assertEquals(autoOffConfig.asConfiguration(), capabilities.getConfiguration()); + assertFalse(capabilities.getConfiguration().isAutoDetectionEnabled()); + assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled()); + } + } + + /** Tests when {@link ConfigurationInternal#isAutoDetectionSupported()} is false. */ + @Test + public void test_autoDetectNotSupported() { + ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) + .setUserConfigAllowed(true) + .setAutoDetectionSupported(false) + .setAutoDetectionEnabled(true) + .setLocationEnabled(true) + .setGeoDetectionEnabled(true) + .build(); + { + ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig) + .setAutoDetectionEnabled(true) + .build(); + assertTrue(autoOnConfig.getAutoDetectionEnabledSetting()); + assertTrue(autoOnConfig.getGeoDetectionEnabledSetting()); + assertFalse(autoOnConfig.getAutoDetectionEnabledBehavior()); + assertFalse(autoOnConfig.getGeoDetectionEnabledBehavior()); + + TimeZoneCapabilities capabilities = autoOnConfig.createCapabilities(); + assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled()); + assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled()); + assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone()); + assertEquals(autoOnConfig.asConfiguration(), capabilities.getConfiguration()); + assertTrue(capabilities.getConfiguration().isAutoDetectionEnabled()); + assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled()); + } + { + ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig) + .setAutoDetectionEnabled(false) + .build(); + assertFalse(autoOffConfig.getAutoDetectionEnabledSetting()); + assertTrue(autoOffConfig.getGeoDetectionEnabledSetting()); + assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior()); + assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior()); + + TimeZoneCapabilities capabilities = autoOffConfig.createCapabilities(); + assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled()); + assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled()); + assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone()); + assertEquals(autoOffConfig.asConfiguration(), capabilities.getConfiguration()); + assertFalse(capabilities.getConfiguration().isAutoDetectionEnabled()); + assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled()); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java index e5e931115c05..4ef20829f2dc 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java @@ -18,6 +18,7 @@ package com.android.server.timezonedetector; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.annotation.NonNull; import android.annotation.UserIdInt; @@ -32,56 +33,64 @@ import java.util.List; class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { - private StrategyListener mListener; + private ConfigurationChangeListener mConfigurationChangeListener; // Fake state - private TimeZoneCapabilities mCapabilities; - private TimeZoneConfiguration mConfiguration; + private ConfigurationInternal mConfigurationInternal; // Call tracking. private GeolocationTimeZoneSuggestion mLastGeolocationSuggestion; private ManualTimeZoneSuggestion mLastManualSuggestion; private TelephonyTimeZoneSuggestion mLastTelephonySuggestion; - private boolean mHandleAutoTimeZoneConfigChangedCalled; private boolean mDumpCalled; private final List<Dumpable> mDumpables = new ArrayList<>(); @Override - public void setStrategyListener(@NonNull StrategyListener listener) { - mListener = listener; + public void addConfigChangeListener(@NonNull ConfigurationChangeListener listener) { + if (mConfigurationChangeListener != null) { + fail("Fake only supports one listener"); + } + mConfigurationChangeListener = listener; + } + + @Override + public ConfigurationInternal getConfigurationInternal(int userId) { + if (mConfigurationInternal.getUserId() != userId) { + fail("Fake only supports one user"); + } + return mConfigurationInternal; } @Override - public TimeZoneCapabilities getCapabilities(@UserIdInt int userId) { - return mCapabilities; + public ConfigurationInternal getCurrentUserConfigurationInternal() { + return mConfigurationInternal; } @Override - public boolean updateConfiguration( - @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration) { - assertNotNull(mConfiguration); - assertNotNull(configuration); - - // Simulate the strategy's behavior: the new configuration will be the old configuration - // merged with the new. - TimeZoneConfiguration oldConfiguration = mConfiguration; - TimeZoneConfiguration newConfiguration = - new TimeZoneConfiguration.Builder(mConfiguration) - .mergeProperties(configuration) - .build(); - - if (newConfiguration.equals(oldConfiguration)) { + public boolean updateConfiguration(@NonNull TimeZoneConfiguration requestedChanges) { + assertNotNull(mConfigurationInternal); + assertNotNull(requestedChanges); + + // Simulate the real strategy's behavior: the new configuration will be updated to be the + // old configuration merged with the new if the user has the capability to up the settings. + // Then, if the configuration changed, the change listener is invoked. + TimeZoneCapabilities capabilities = mConfigurationInternal.createCapabilities(); + TimeZoneConfiguration newConfiguration = capabilities.applyUpdate(requestedChanges); + if (newConfiguration == null) { return false; } - mConfiguration = newConfiguration; - mListener.onConfigurationChanged(); + + if (!newConfiguration.equals(capabilities.getConfiguration())) { + mConfigurationInternal = mConfigurationInternal.merge(newConfiguration); + + // Note: Unlike the real strategy, the listeners is invoked synchronously. + mConfigurationChangeListener.onChange(); + } return true; } - @Override - @NonNull - public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) { - return mConfiguration; + public void simulateConfigurationChangeForTests() { + mConfigurationChangeListener.onChange(); } @Override @@ -103,11 +112,6 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { } @Override - public void handleAutoTimeZoneConfigChanged() { - mHandleAutoTimeZoneConfigChangedCalled = true; - } - - @Override public void addDumpable(Dumpable dumpable) { mDumpables.add(dumpable); } @@ -117,19 +121,14 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { mDumpCalled = true; } - void initializeConfiguration(TimeZoneConfiguration configuration) { - mConfiguration = configuration; - } - - void initializeCapabilities(TimeZoneCapabilities capabilities) { - mCapabilities = capabilities; + void initializeConfiguration(ConfigurationInternal configurationInternal) { + mConfigurationInternal = configurationInternal; } void resetCallTracking() { mLastGeolocationSuggestion = null; mLastManualSuggestion = null; mLastTelephonySuggestion = null; - mHandleAutoTimeZoneConfigChangedCalled = false; mDumpCalled = false; } @@ -146,10 +145,6 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { assertEquals(expectedSuggestion, mLastTelephonySuggestion); } - void verifyHandleAutoTimeZoneConfigChangedCalled() { - assertTrue(mHandleAutoTimeZoneConfigChangedCalled); - } - void verifyDumpCalled() { assertTrue(mDumpCalled); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java new file mode 100644 index 000000000000..f45b3a822f1a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java @@ -0,0 +1,53 @@ +/* + * 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 com.android.server.timezonedetector; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.annotation.UserIdInt; + +/** A fake {@link CallerIdentityInjector} used in tests. */ +public class TestCallerIdentityInjector implements CallerIdentityInjector { + + private long mToken = 9999L; + private int mCallingUserId; + private Integer mCurrentCallingUserId; + + public void initializeCallingUserId(@UserIdInt int userId) { + mCallingUserId = userId; + mCurrentCallingUserId = userId; + } + + @Override + public int getCallingUserId() { + assertNotNull("callingUserId has been cleared", mCurrentCallingUserId); + return mCurrentCallingUserId; + } + + @Override + public long clearCallingIdentity() { + mCurrentCallingUserId = null; + return mToken; + } + + @Override + public void restoreCallingIdentity(long token) { + assertEquals(token, mToken); + mCurrentCallingUserId = mCallingUserId; + } +} diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java index e9d57e52ce69..918babca677e 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java @@ -16,6 +16,7 @@ package com.android.server.timezonedetector; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import android.content.Context; @@ -85,6 +86,18 @@ public class TimeZoneDetectorInternalImplTest { mFakeTimeZoneDetectorStrategy.verifyHasDumpable(stubbedDumpable); } + @Test + public void testAddConfigurationListener() throws Exception { + boolean[] changeCalled = new boolean[2]; + mTimeZoneDetectorInternal.addConfigurationListener(() -> changeCalled[0] = true); + mTimeZoneDetectorInternal.addConfigurationListener(() -> changeCalled[1] = true); + + mFakeTimeZoneDetectorStrategy.simulateConfigurationChangeForTests(); + + assertTrue(changeCalled[0]); + assertTrue(changeCalled[1]); + } + private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() { return new GeolocationTimeZoneSuggestion(ARBITRARY_ZONE_IDS); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java index 3a1ec4f90d7a..27b04b6ab17d 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java @@ -16,8 +16,6 @@ package com.android.server.timezonedetector; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -36,7 +34,6 @@ import static org.mockito.Mockito.when; import android.app.timezonedetector.ITimeZoneConfigurationListener; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; -import android.app.timezonedetector.TimeZoneCapabilities; import android.app.timezonedetector.TimeZoneConfiguration; import android.content.Context; import android.content.pm.PackageManager; @@ -65,6 +62,7 @@ public class TimeZoneDetectorServiceTest { private TimeZoneDetectorService mTimeZoneDetectorService; private HandlerThread mHandlerThread; private TestHandler mTestHandler; + private TestCallerIdentityInjector mTestCallerIdentityInjector; @Before @@ -76,10 +74,14 @@ public class TimeZoneDetectorServiceTest { mHandlerThread.start(); mTestHandler = new TestHandler(mHandlerThread.getLooper()); + mTestCallerIdentityInjector = new TestCallerIdentityInjector(); + mTestCallerIdentityInjector.initializeCallingUserId(ARBITRARY_USER_ID); + mFakeTimeZoneDetectorStrategy = new FakeTimeZoneDetectorStrategy(); mTimeZoneDetectorService = new TimeZoneDetectorService( - mMockContext, mTestHandler, mFakeTimeZoneDetectorStrategy); + mMockContext, mTestHandler, mTestCallerIdentityInjector, + mFakeTimeZoneDetectorStrategy); } @After @@ -107,40 +109,12 @@ public class TimeZoneDetectorServiceTest { public void testGetCapabilities() { doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); - TimeZoneCapabilities capabilities = createTimeZoneCapabilities(); - mFakeTimeZoneDetectorStrategy.initializeCapabilities(capabilities); - - assertEquals(capabilities, mTimeZoneDetectorService.getCapabilities()); - - verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), - anyString()); - } - - @Test(expected = SecurityException.class) - public void testGetConfiguration_withoutPermission() { - doThrow(new SecurityException("Mock")) - .when(mMockContext).enforceCallingPermission(anyString(), any()); - - try { - mTimeZoneDetectorService.getConfiguration(); - fail(); - } finally { - verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), - anyString()); - } - } - - @Test - public void testGetConfiguration() { - doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); - - TimeZoneConfiguration configuration = - createTimeZoneConfiguration(false /* autoDetectionEnabled */); + ConfigurationInternal configuration = + createConfigurationInternal(true /* autoDetectionEnabled*/); mFakeTimeZoneDetectorStrategy.initializeConfiguration(configuration); - assertEquals(configuration, mTimeZoneDetectorService.getConfiguration()); + assertEquals(configuration.createCapabilities(), + mTimeZoneDetectorService.getCapabilities()); verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), @@ -181,10 +155,9 @@ public class TimeZoneDetectorServiceTest { @Test public void testConfigurationChangeListenerRegistrationAndCallbacks() throws Exception { - TimeZoneConfiguration autoDetectDisabledConfiguration = - createTimeZoneConfiguration(false /* autoDetectionEnabled */); - - mFakeTimeZoneDetectorStrategy.initializeConfiguration(autoDetectDisabledConfiguration); + ConfigurationInternal initialConfiguration = + createConfigurationInternal(false /* autoDetectionEnabled */); + mFakeTimeZoneDetectorStrategy.initializeConfiguration(initialConfiguration); IBinder mockListenerBinder = mock(IBinder.class); ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class); @@ -210,13 +183,12 @@ public class TimeZoneDetectorServiceTest { // Simulate the configuration being changed and verify the mockListener was notified. TimeZoneConfiguration autoDetectEnabledConfiguration = createTimeZoneConfiguration(true /* autoDetectionEnabled */); - mTimeZoneDetectorService.updateConfiguration(autoDetectEnabledConfiguration); verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), anyString()); - verify(mockListener).onChange(autoDetectEnabledConfiguration); + verify(mockListener).onChange(); verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext); reset(mockListenerBinder, mockListener, mMockContext); } @@ -242,12 +214,14 @@ public class TimeZoneDetectorServiceTest { { doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); + TimeZoneConfiguration autoDetectDisabledConfiguration = + createTimeZoneConfiguration(false /* autoDetectionEnabled */); mTimeZoneDetectorService.updateConfiguration(autoDetectDisabledConfiguration); verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), anyString()); - verify(mockListener, never()).onChange(any()); + verify(mockListener, never()).onChange(); verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext); reset(mockListenerBinder, mockListener, mMockContext); } @@ -379,33 +353,22 @@ public class TimeZoneDetectorServiceTest { mFakeTimeZoneDetectorStrategy.verifyDumpCalled(); } - @Test - public void testHandleAutoTimeZoneConfigChanged() throws Exception { - mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged(); - mTestHandler.assertTotalMessagesEnqueued(1); - mTestHandler.waitForMessagesToBeProcessed(); - mFakeTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled(); - - mFakeTimeZoneDetectorStrategy.resetCallTracking(); - - mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged(); - mTestHandler.assertTotalMessagesEnqueued(2); - mTestHandler.waitForMessagesToBeProcessed(); - mFakeTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled(); - } - - private static TimeZoneConfiguration createTimeZoneConfiguration( - boolean autoDetectionEnabled) { - return new TimeZoneConfiguration.Builder() + private static TimeZoneConfiguration createTimeZoneConfiguration(boolean autoDetectionEnabled) { + return new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) .setAutoDetectionEnabled(autoDetectionEnabled) .build(); } - private static TimeZoneCapabilities createTimeZoneCapabilities() { - return new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID) - .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) - .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) - .setSuggestManualTimeZone(CAPABILITY_POSSESSED) + private static ConfigurationInternal createConfigurationInternal(boolean autoDetectionEnabled) { + // Default geo detection settings from auto detection settings - they are not important to + // the tests. + final boolean geoDetectionEnabled = autoDetectionEnabled; + return new ConfigurationInternal.Builder(ARBITRARY_USER_ID) + .setAutoDetectionSupported(true) + .setUserConfigAllowed(true) + .setAutoDetectionEnabled(autoDetectionEnabled) + .setLocationEnabled(geoDetectionEnabled) + .setGeoDetectionEnabled(geoDetectionEnabled) .build(); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java index a6caa4299ef6..2bee5e5e7295 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java @@ -23,10 +23,6 @@ import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYP import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED; import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGH; import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGHEST; @@ -37,9 +33,9 @@ import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.T import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -47,7 +43,6 @@ import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType; import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality; -import android.app.timezonedetector.TimeZoneCapabilities; import android.app.timezonedetector.TimeZoneConfiguration; import android.util.IndentingPrintWriter; @@ -61,7 +56,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; -import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -94,192 +88,149 @@ public class TimeZoneDetectorStrategyImplTest { TELEPHONY_SCORE_HIGHEST), }; - private static final TimeZoneConfiguration CONFIG_AUTO_ENABLED = - new TimeZoneConfiguration.Builder() + private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_DISABLED = + new ConfigurationInternal.Builder(USER_ID) + .setUserConfigAllowed(false) + .setAutoDetectionSupported(true) + .setAutoDetectionEnabled(false) + .setLocationEnabled(true) + .setGeoDetectionEnabled(false) + .build(); + + private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED = + new ConfigurationInternal.Builder(USER_ID) + .setUserConfigAllowed(false) + .setAutoDetectionSupported(true) .setAutoDetectionEnabled(true) + .setLocationEnabled(true) + .setGeoDetectionEnabled(true) .build(); - private static final TimeZoneConfiguration CONFIG_AUTO_DISABLED = - new TimeZoneConfiguration.Builder() + private static final ConfigurationInternal CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED = + new ConfigurationInternal.Builder(USER_ID) + .setUserConfigAllowed(true) + .setAutoDetectionSupported(false) .setAutoDetectionEnabled(false) + .setLocationEnabled(true) + .setGeoDetectionEnabled(false) .build(); - private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_DISABLED = - new TimeZoneConfiguration.Builder() + private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_DISABLED = + new ConfigurationInternal.Builder(USER_ID) + .setUserConfigAllowed(true) + .setAutoDetectionSupported(true) + .setAutoDetectionEnabled(false) + .setLocationEnabled(true) .setGeoDetectionEnabled(false) .build(); - private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_ENABLED = - new TimeZoneConfiguration.Builder() + private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_ENABLED = + new ConfigurationInternal.Builder(USER_ID) + .setUserConfigAllowed(true) + .setAutoDetectionSupported(true) + .setAutoDetectionEnabled(false) + .setLocationEnabled(true) .setGeoDetectionEnabled(true) .build(); + private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_DISABLED = + new ConfigurationInternal.Builder(USER_ID) + .setAutoDetectionSupported(true) + .setUserConfigAllowed(true) + .setAutoDetectionEnabled(true) + .setLocationEnabled(true) + .setGeoDetectionEnabled(false) + .build(); + + private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_ENABLED = + new ConfigurationInternal.Builder(USER_ID) + .setAutoDetectionSupported(true) + .setUserConfigAllowed(true) + .setAutoDetectionEnabled(true) + .setLocationEnabled(true) + .setGeoDetectionEnabled(true) + .build(); + + private static final TimeZoneConfiguration CONFIG_AUTO_DISABLED = + createConfig(false /* autoDetection */, null); + private static final TimeZoneConfiguration CONFIG_AUTO_ENABLED = + createConfig(true /* autoDetection */, null); + private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_ENABLED = + createConfig(null, true /* geoDetection */); + private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_DISABLED = + createConfig(null, false /* geoDetection */); + private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy; private FakeCallback mFakeCallback; - private MockStrategyListener mMockStrategyListener; + private MockConfigChangeListener mMockConfigChangeListener; + @Before public void setUp() { mFakeCallback = new FakeCallback(); - mMockStrategyListener = new MockStrategyListener(); + mMockConfigChangeListener = new MockConfigChangeListener(); mTimeZoneDetectorStrategy = new TimeZoneDetectorStrategyImpl(mFakeCallback); - mFakeCallback.setStrategyForSettingsCallbacks(mTimeZoneDetectorStrategy); - mTimeZoneDetectorStrategy.setStrategyListener(mMockStrategyListener); - } - - @Test - public void testGetCapabilities() { - new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); - TimeZoneCapabilities expectedCapabilities = mFakeCallback.getCapabilities(USER_ID); - assertEquals(expectedCapabilities, mTimeZoneDetectorStrategy.getCapabilities(USER_ID)); + mTimeZoneDetectorStrategy.addConfigChangeListener(mMockConfigChangeListener); } @Test - public void testGetConfiguration() { - new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); - TimeZoneConfiguration expectedConfiguration = mFakeCallback.getConfiguration(USER_ID); - assertTrue(expectedConfiguration.isComplete()); - assertEquals(expectedConfiguration, mTimeZoneDetectorStrategy.getConfiguration(USER_ID)); - } - - @Test - public void testCapabilitiesTestInfra_unrestricted() { - Script script = new Script(); - - script.initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); - { - // Check the fake test infra is doing what is expected. - TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); - assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSuggestManualTimeZone()); - } - - script.initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)); - { - // Check the fake test infra is doing what is expected. - TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); - assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone()); - } - } - - @Test - public void testCapabilitiesTestInfra_restricted() { - Script script = new Script(); - - script.initializeUser(USER_ID, UserCase.RESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); - { - // Check the fake test infra is doing what is expected. - TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone()); - } - - script.initializeUser(USER_ID, UserCase.RESTRICTED, - CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)); - { - // Check the fake test infra is doing what is expected. - TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone()); - } - } - - @Test - public void testCapabilitiesTestInfra_autoDetectNotSupported() { - Script script = new Script(); - - script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); - { - // Check the fake test infra is doing what is expected. - TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); - assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone()); - } - - script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, - CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)); - { - // Check the fake test infra is doing what is expected. - TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); - assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone()); - } + public void testGetCurrentUserConfiguration() { + new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED); + ConfigurationInternal expectedConfiguration = + mFakeCallback.getConfigurationInternal(USER_ID); + assertEquals(expectedConfiguration, + mTimeZoneDetectorStrategy.getCurrentUserConfigurationInternal()); } @Test public void testUpdateConfiguration_unrestricted() { - Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); + Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED); // Set the configuration with auto detection enabled. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */); + script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */); // Nothing should have happened: it was initialized in this state. script.verifyConfigurationNotChanged(); // Update the configuration with auto detection disabled. - script.simulateUpdateConfiguration( - USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */); + script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */); // The settings should have been changed and the StrategyListener onChange() called. - script.verifyConfigurationChangedAndReset(USER_ID, - CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)); + script.verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED); // Update the configuration with auto detection enabled. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */); + script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */); // The settings should have been changed and the StrategyListener onChange() called. - script.verifyConfigurationChangedAndReset(USER_ID, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); + script.verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED); // Update the configuration to enable geolocation time zone detection. script.simulateUpdateConfiguration( - USER_ID, CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */); + CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */); // The settings should have been changed and the StrategyListener onChange() called. - script.verifyConfigurationChangedAndReset(USER_ID, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED)); + script.verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED); } @Test public void testUpdateConfiguration_restricted() { - Script script = new Script() - .initializeUser(USER_ID, UserCase.RESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); + Script script = new Script().initializeConfig(CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED); // Try to update the configuration with auto detection disabled. - script.simulateUpdateConfiguration( - USER_ID, CONFIG_AUTO_DISABLED, false /* expectedResult */); + script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); // Update the configuration with auto detection enabled. - script.simulateUpdateConfiguration( - USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */); + script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); - // Update the configuration to enable geolocation time zone detection. + // Try to update the configuration to enable geolocation time zone detection. script.simulateUpdateConfiguration( - USER_ID, CONFIG_GEO_DETECTION_ENABLED, false /* expectedResult */); + CONFIG_GEO_DETECTION_ENABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); @@ -287,20 +238,16 @@ public class TimeZoneDetectorStrategyImplTest { @Test public void testUpdateConfiguration_autoDetectNotSupported() { - Script script = new Script() - .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); + Script script = new Script().initializeConfig(CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED); // Try to update the configuration with auto detection disabled. - script.simulateUpdateConfiguration( - USER_ID, CONFIG_AUTO_DISABLED, false /* expectedResult */); + script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); // Update the configuration with auto detection enabled. - script.simulateUpdateConfiguration( - USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */); + script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); @@ -313,8 +260,7 @@ public class TimeZoneDetectorStrategyImplTest { TelephonyTimeZoneSuggestion slotIndex2TimeZoneSuggestion = createEmptySlotIndex2Suggestion(); Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)) + .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); script.simulateTelephonyTimeZoneSuggestion(slotIndex1TimeZoneSuggestion) @@ -359,9 +305,7 @@ public class TimeZoneDetectorStrategyImplTest { TelephonyTestCase testCase2 = newTelephonyTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH); - Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); + Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED); // A low quality suggestions will not be taken: The device time zone setting is left // uninitialized. @@ -426,8 +370,7 @@ public class TimeZoneDetectorStrategyImplTest { for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) { // Start with the device in a known state. - script.initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)) + script.initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); TelephonyTimeZoneSuggestion suggestion = @@ -447,8 +390,7 @@ public class TimeZoneDetectorStrategyImplTest { mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); // Toggling the time zone setting on should cause the device setting to be set. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, - true /* expectedResult */); + script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */); // When time zone detection is already enabled the suggestion (if it scores highly // enough) should be set immediately. @@ -465,8 +407,7 @@ public class TimeZoneDetectorStrategyImplTest { mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); // Toggling the time zone setting should off should do nothing. - script.simulateUpdateConfiguration( - USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) + script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */) .verifyTimeZoneNotChanged(); // Assert internal service state. @@ -480,8 +421,7 @@ public class TimeZoneDetectorStrategyImplTest { @Test public void testTelephonySuggestionsSingleSlotId() { Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)) + .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) { @@ -546,8 +486,7 @@ public class TimeZoneDetectorStrategyImplTest { TELEPHONY_SCORE_NONE); Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)) + .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID) // Initialize the latest suggestions as empty so we don't need to worry about nulls // below for the first loop. @@ -632,9 +571,7 @@ public class TimeZoneDetectorStrategyImplTest { */ @Test public void testTelephonySuggestionStrategyDoesNotAssumeCurrentSetting_autoTelephony() { - Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); + Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED); TelephonyTestCase testCase = newTelephonyTestCase( MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH); @@ -652,40 +589,39 @@ public class TimeZoneDetectorStrategyImplTest { // Toggling time zone detection should set the device time zone only if the current setting // value is different from the most recent telephony suggestion. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) + script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */) .verifyTimeZoneNotChanged() - .simulateUpdateConfiguration( - USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) + .simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */) .verifyTimeZoneNotChanged(); // Simulate a user turning auto detection off, a new suggestion being made while auto // detection is off, and the user turning it on again. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) + script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */) .simulateTelephonyTimeZoneSuggestion(newYorkSuggestion) .verifyTimeZoneNotChanged(); // Latest suggestion should be used. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) + script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */) .verifyTimeZoneChangedAndReset(newYorkSuggestion); } @Test - public void testManualSuggestion_autoDetectionEnabled_autoTelephony() { - checkManualSuggestion_autoDetectionEnabled(false /* geoDetectionEnabled */); + public void testManualSuggestion_unrestricted_autoDetectionEnabled_autoTelephony() { + checkManualSuggestion_unrestricted_autoDetectionEnabled(false /* geoDetectionEnabled */); } @Test - public void testManualSuggestion_autoDetectionEnabled_autoGeo() { - checkManualSuggestion_autoDetectionEnabled(true /* geoDetectionEnabled */); + public void testManualSuggestion_unrestricted_autoDetectionEnabled_autoGeo() { + checkManualSuggestion_unrestricted_autoDetectionEnabled(true /* geoDetectionEnabled */); } - private void checkManualSuggestion_autoDetectionEnabled(boolean geoDetectionEnabled) { - TimeZoneConfiguration geoTzEnabledConfig = - new TimeZoneConfiguration.Builder() + private void checkManualSuggestion_unrestricted_autoDetectionEnabled( + boolean geoDetectionEnabled) { + ConfigurationInternal geoTzEnabledConfig = + new ConfigurationInternal.Builder(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED) .setGeoDetectionEnabled(geoDetectionEnabled) .build(); Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(geoTzEnabledConfig)) + .initializeConfig(geoTzEnabledConfig) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); // Auto time zone detection is enabled so the manual suggestion should be ignored. @@ -697,35 +633,19 @@ public class TimeZoneDetectorStrategyImplTest { @Test public void testManualSuggestion_restricted_simulateAutoTimeZoneEnabled() { Script script = new Script() - .initializeUser(USER_ID, UserCase.RESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)) + .initializeConfig(CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); - // Auto time zone detection is enabled so the manual suggestion should be ignored. + // User is restricted so the manual suggestion should be ignored. script.simulateManualTimeZoneSuggestion( USER_ID, createManualSuggestion("Europe/Paris"), false /* expectedResult */) - .verifyTimeZoneNotChanged(); - } - - @Test - public void testManualSuggestion_autoDetectNotSupported_simulateAutoTimeZoneEnabled() { - Script script = new Script() - .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)) - .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); - - // Auto time zone detection is enabled so the manual suggestion should be ignored. - ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris"); - script.simulateManualTimeZoneSuggestion( - USER_ID, manualSuggestion, true /* expectedResult */) - .verifyTimeZoneChangedAndReset(manualSuggestion); + .verifyTimeZoneNotChanged(); } @Test public void testManualSuggestion_unrestricted_autoTimeZoneDetectionDisabled() { Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)) + .initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); // Auto time zone detection is disabled so the manual suggestion should be used. @@ -738,8 +658,7 @@ public class TimeZoneDetectorStrategyImplTest { @Test public void testManualSuggestion_restricted_autoTimeZoneDetectionDisabled() { Script script = new Script() - .initializeUser(USER_ID, UserCase.RESTRICTED, - CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)) + .initializeConfig(CONFIG_INT_USER_RESTRICTED_AUTO_DISABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); // Restricted users do not have the capability. @@ -750,10 +669,9 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testManualSuggestion_autoDetectNotSupported_autoTimeZoneDetectionDisabled() { + public void testManualSuggestion_autoDetectNotSupported() { Script script = new Script() - .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, - CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)) + .initializeConfig(CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); // Unrestricted users have the capability. @@ -765,9 +683,7 @@ public class TimeZoneDetectorStrategyImplTest { @Test public void testGeoSuggestion_uncertain() { - Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED)) + Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); GeolocationTimeZoneSuggestion uncertainSuggestion = createUncertainGeoLocationSuggestion(); @@ -783,8 +699,7 @@ public class TimeZoneDetectorStrategyImplTest { @Test public void testGeoSuggestion_noZones() { Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED)) + .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); GeolocationTimeZoneSuggestion noZonesSuggestion = createGeoLocationSuggestion(list()); @@ -802,8 +717,7 @@ public class TimeZoneDetectorStrategyImplTest { createGeoLocationSuggestion(list("Europe/London")); Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED)) + .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); script.simulateGeolocationTimeZoneSuggestion(suggestion) @@ -828,8 +742,7 @@ public class TimeZoneDetectorStrategyImplTest { createGeoLocationSuggestion(list("Europe/Paris")); Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED)) + .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); script.simulateGeolocationTimeZoneSuggestion(londonOnlySuggestion) @@ -856,72 +769,27 @@ public class TimeZoneDetectorStrategyImplTest { mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); } - /** - * Confirms that toggling the auto time zone detection enabled setting has the expected behavior - * when the strategy is "opinionated" and "un-opinionated" when in geolocation detection is - * enabled. - */ @Test - public void testTogglingAutoDetectionEnabled_autoGeo() { - GeolocationTimeZoneSuggestion geolocationSuggestion = + public void testGeoSuggestion_togglingGeoDetectionClearsLastSuggestion() { + GeolocationTimeZoneSuggestion suggestion = createGeoLocationSuggestion(list("Europe/London")); - GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion = - createUncertainGeoLocationSuggestion(); - ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris"); Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_ENABLED)) + .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); - script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion); - - // When time zone detection is not enabled, the time zone suggestion will not be set. - script.verifyTimeZoneNotChanged(); - - // Assert internal service state. - assertEquals(geolocationSuggestion, - mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); - - // Toggling the time zone setting on should cause the device setting to be set. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) + script.simulateGeolocationTimeZoneSuggestion(suggestion) .verifyTimeZoneChangedAndReset("Europe/London"); - // Toggling the time zone setting should off should do nothing because the device is now - // set to that time zone. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) - .verifyTimeZoneNotChanged() - .simulateUpdateConfiguration( - USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) - .verifyTimeZoneNotChanged(); - - // Now toggle auto time zone setting, and confirm it is opinionated. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) - .simulateManualTimeZoneSuggestion( - USER_ID, manualSuggestion, true /* expectedResult */) - .verifyTimeZoneChangedAndReset(manualSuggestion) - .simulateUpdateConfiguration( - USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) - .verifyTimeZoneChangedAndReset("Europe/London"); + // Assert internal service state. + assertEquals(suggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); - // Now withdraw the geolocation suggestion, and assert the strategy is no longer - // opinionated. - /* expectedResult */ - script.simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion) - .verifyTimeZoneNotChanged() - .simulateUpdateConfiguration( - USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) - .verifyTimeZoneNotChanged() - .simulateManualTimeZoneSuggestion( - USER_ID, manualSuggestion, true /* expectedResult */) - .verifyTimeZoneChangedAndReset(manualSuggestion) - .simulateUpdateConfiguration( - USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) - .verifyTimeZoneNotChanged(); + // Turn off geo detection and verify the latest suggestion is cleared. + script.simulateUpdateConfiguration(CONFIG_GEO_DETECTION_DISABLED, true) + .verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED); // Assert internal service state. - assertEquals(uncertainGeolocationSuggestion, - mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); + assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); } /** @@ -937,88 +805,48 @@ public class TimeZoneDetectorStrategyImplTest { "Europe/Paris"); Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)) + .initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); // Add suggestions. Nothing should happen as time zone detection is disabled. script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion) .verifyTimeZoneNotChanged(); + + // Geolocation suggestions are only stored when geolocation detection is enabled. + assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); + script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion) .verifyTimeZoneNotChanged(); - // Assert internal service state. - assertEquals(geolocationSuggestion, - mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); + // Telephony suggestions are always stored. assertEquals(telephonySuggestion, mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1).suggestion); // Toggling the time zone detection enabled setting on should cause the device setting to be // set from the telephony signal, as we've started with geolocation time zone detection // disabled. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) + script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */) .verifyTimeZoneChangedAndReset(telephonySuggestion); - // Changing the detection to enable geo detection should cause the device tz setting to - // change to the geo suggestion. - script.simulateUpdateConfiguration( - USER_ID, CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */) + // Changing the detection to enable geo detection won't cause the device tz setting to + // change because the geo suggestion is empty. + script.simulateUpdateConfiguration(CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */) + .verifyTimeZoneNotChanged() + .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion) .verifyTimeZoneChangedAndReset(geolocationSuggestion.getZoneIds().get(0)); // Changing the detection to disable geo detection should cause the device tz setting to // change to the telephony suggestion. - script.simulateUpdateConfiguration( - USER_ID, CONFIG_GEO_DETECTION_DISABLED, true /* expectedResult */) + script.simulateUpdateConfiguration(CONFIG_GEO_DETECTION_DISABLED, true /* expectedResult */) .verifyTimeZoneChangedAndReset(telephonySuggestion); - } - /** - * The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing the time - * zone is actually necessary. This test proves that the strategy doesn't assume it knows the - * current setting. - */ - @Test - public void testTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting_autoGeo() { - GeolocationTimeZoneSuggestion losAngelesSuggestion = - createGeoLocationSuggestion(list("America/Los_Angeles")); - GeolocationTimeZoneSuggestion newYorkSuggestion = - createGeoLocationSuggestion(list("America/New_York")); - - Script script = new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED)); - - // Initialization. - script.simulateGeolocationTimeZoneSuggestion(losAngelesSuggestion) - .verifyTimeZoneChangedAndReset("America/Los_Angeles"); - // Suggest it again - it should not be set because it is already set. - script.simulateGeolocationTimeZoneSuggestion(losAngelesSuggestion) - .verifyTimeZoneNotChanged(); - - // Toggling time zone detection should set the device time zone only if the current setting - // value is different from the most recent telephony suggestion. - /* expectedResult */ - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) - .verifyTimeZoneNotChanged() - .simulateUpdateConfiguration( - USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) - .verifyTimeZoneNotChanged(); - - // Simulate a user turning auto detection off, a new suggestion being made while auto - // detection is off, and the user turning it on again. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) - .simulateGeolocationTimeZoneSuggestion(newYorkSuggestion) - .verifyTimeZoneNotChanged(); - // Latest suggestion should be used. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) - .verifyTimeZoneChangedAndReset("America/New_York"); + assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); } @Test public void testAddDumpable() { new Script() - .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)) + .initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); AtomicBoolean dumpCalled = new AtomicBoolean(false); @@ -1069,25 +897,26 @@ public class TimeZoneDetectorStrategyImplTest { return suggestion; } + private static TimeZoneConfiguration createConfig( + @Nullable Boolean autoDetection, @Nullable Boolean geoDetection) { + TimeZoneConfiguration.Builder builder = new TimeZoneConfiguration.Builder(USER_ID); + if (autoDetection != null) { + builder.setAutoDetectionEnabled(autoDetection); + } + if (geoDetection != null) { + builder.setGeoDetectionEnabled(geoDetection); + } + return builder.build(); + } + static class FakeCallback implements TimeZoneDetectorStrategyImpl.Callback { - private TimeZoneCapabilities mCapabilities; - private final TestState<UserConfiguration> mConfiguration = new TestState<>(); + private final TestState<ConfigurationInternal> mConfigurationInternal = new TestState<>(); private final TestState<String> mTimeZoneId = new TestState<>(); - private TimeZoneDetectorStrategyImpl mStrategy; - - void setStrategyForSettingsCallbacks(TimeZoneDetectorStrategyImpl strategy) { - assertNotNull(strategy); - mStrategy = strategy; - } + private ConfigurationChangeListener mConfigChangeListener; - void initializeUser(@UserIdInt int userId, TimeZoneCapabilities capabilities, - TimeZoneConfiguration configuration) { - assertEquals(userId, capabilities.getUserId()); - mCapabilities = capabilities; - assertTrue("Configuration must be complete when initializing, config=" + configuration, - configuration.isComplete()); - mConfiguration.init(new UserConfiguration(userId, configuration)); + void initializeConfig(ConfigurationInternal configurationInternal) { + mConfigurationInternal.init(configurationInternal); } void initializeTimeZoneSetting(String zoneId) { @@ -1095,43 +924,22 @@ public class TimeZoneDetectorStrategyImplTest { } @Override - public TimeZoneCapabilities getCapabilities(@UserIdInt int userId) { - assertEquals(userId, mCapabilities.getUserId()); - return mCapabilities; + public void setConfigChangeListener(ConfigurationChangeListener listener) { + mConfigChangeListener = listener; } @Override - public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) { - UserConfiguration latest = mConfiguration.getLatest(); - assertEquals(userId, latest.userId); - return latest.configuration; - } - - @Override - public void setConfiguration(@UserIdInt int userId, TimeZoneConfiguration newConfig) { - assertNotNull(newConfig); - assertTrue(newConfig.isComplete()); - - UserConfiguration latestUserConfig = mConfiguration.getLatest(); - assertEquals(userId, latestUserConfig.userId); - TimeZoneConfiguration oldConfig = latestUserConfig.configuration; - - mConfiguration.set(new UserConfiguration(userId, newConfig)); - - if (!newConfig.equals(oldConfig)) { - // Simulate what happens when the auto detection configuration is changed. - mStrategy.handleAutoTimeZoneConfigChanged(); + public ConfigurationInternal getConfigurationInternal(int userId) { + ConfigurationInternal configuration = mConfigurationInternal.getLatest(); + if (userId != configuration.getUserId()) { + fail("FakeCallback does not support multiple users."); } + return configuration; } @Override - public boolean isAutoDetectionEnabled() { - return mConfiguration.getLatest().configuration.isAutoDetectionEnabled(); - } - - @Override - public boolean isGeoDetectionEnabled() { - return mConfiguration.getLatest().configuration.isGeoDetectionEnabled(); + public int getCurrentUserId() { + return mConfigurationInternal.getLatest().getUserId(); } @Override @@ -1149,9 +957,25 @@ public class TimeZoneDetectorStrategyImplTest { mTimeZoneId.set(zoneId); } + @Override + public void storeConfiguration(TimeZoneConfiguration newConfiguration) { + ConfigurationInternal oldConfiguration = mConfigurationInternal.getLatest(); + if (newConfiguration.getUserId() != oldConfiguration.getUserId()) { + fail("FakeCallback does not support multiple users"); + } + + ConfigurationInternal mergedConfiguration = oldConfiguration.merge(newConfiguration); + if (!mergedConfiguration.equals(oldConfiguration)) { + mConfigurationInternal.set(mergedConfiguration); + + // Note: Unlike the real callback impl, the listener is invoked synchronously. + mConfigChangeListener.onChange(); + } + } + void assertKnownUser(int userId) { - assertEquals(userId, mCapabilities.getUserId()); - assertEquals(userId, mConfiguration.getLatest().userId); + assertEquals("FakeCallback does not support multiple users", + mConfigurationInternal.getLatest().getUserId(), userId); } void assertTimeZoneNotChanged() { @@ -1166,43 +990,7 @@ public class TimeZoneDetectorStrategyImplTest { void commitAllChanges() { mTimeZoneId.commitLatest(); - mConfiguration.commitLatest(); - } - } - - private static final class UserConfiguration { - public final @UserIdInt int userId; - public final TimeZoneConfiguration configuration; - - UserConfiguration(int userId, TimeZoneConfiguration configuration) { - this.userId = userId; - this.configuration = configuration; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - UserConfiguration that = (UserConfiguration) o; - return userId == that.userId - && Objects.equals(configuration, that.configuration); - } - - @Override - public int hashCode() { - return Objects.hash(userId, configuration); - } - - @Override - public String toString() { - return "UserConfiguration{" - + "userId=" + userId - + ", configuration=" + configuration - + '}'; + mConfigurationInternal.commitLatest(); } } @@ -1255,64 +1043,14 @@ public class TimeZoneDetectorStrategyImplTest { } } - /** Simulated user test cases. */ - enum UserCase { - /** A catch-all for users that can set auto time zone config. */ - UNRESTRICTED, - /** A catch-all for users that can't set auto time zone config. */ - RESTRICTED, - /** - * Like {@link #UNRESTRICTED}, but auto tz detection is not - * supported on the device. - */ - AUTO_DETECT_NOT_SUPPORTED, - } - - /** - * Creates a {@link TimeZoneCapabilities} object for a user in the specific role with the - * supplied configuration. - */ - private static TimeZoneCapabilities createCapabilities( - int userId, UserCase userCase, TimeZoneConfiguration configuration) { - switch (userCase) { - case UNRESTRICTED: { - int suggestManualTimeZoneCapability = configuration.isAutoDetectionEnabled() - ? CAPABILITY_NOT_APPLICABLE : CAPABILITY_POSSESSED; - return new TimeZoneCapabilities.Builder(userId) - .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) - .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) - .setSuggestManualTimeZone(suggestManualTimeZoneCapability) - .build(); - } - case RESTRICTED: { - return new TimeZoneCapabilities.Builder(userId) - .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED) - .setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED) - .setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED) - .build(); - } - case AUTO_DETECT_NOT_SUPPORTED: { - return new TimeZoneCapabilities.Builder(userId) - .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_SUPPORTED) - .setConfigureGeoDetectionEnabled(CAPABILITY_NOT_SUPPORTED) - .setSuggestManualTimeZone(CAPABILITY_POSSESSED) - .build(); - } - default: - throw new AssertionError(userCase + " not recognized"); - } - } - /** * A "fluent" class allows reuse of code in tests: initialization, simulation and verification * logic. */ private class Script { - Script initializeUser( - @UserIdInt int userId, UserCase userCase, TimeZoneConfiguration configuration) { - TimeZoneCapabilities capabilities = createCapabilities(userId, userCase, configuration); - mFakeCallback.initializeUser(userId, capabilities, configuration); + Script initializeConfig(ConfigurationInternal configuration) { + mFakeCallback.initializeConfig(configuration); return this; } @@ -1326,10 +1064,9 @@ public class TimeZoneDetectorStrategyImplTest { * the return value. */ Script simulateUpdateConfiguration( - @UserIdInt int userId, TimeZoneConfiguration configuration, - boolean expectedResult) { + TimeZoneConfiguration configuration, boolean expectedResult) { assertEquals(expectedResult, - mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration)); + mTimeZoneDetectorStrategy.updateConfiguration(configuration)); return this; } @@ -1392,16 +1129,14 @@ public class TimeZoneDetectorStrategyImplTest { /** * Verifies that the configuration has been changed to the expected value. */ - Script verifyConfigurationChangedAndReset( - @UserIdInt int userId, TimeZoneConfiguration expected) { - mFakeCallback.mConfiguration.assertHasBeenSet(); - UserConfiguration expectedUserConfig = new UserConfiguration(userId, expected); - assertEquals(expectedUserConfig, mFakeCallback.mConfiguration.getLatest()); + Script verifyConfigurationChangedAndReset(ConfigurationInternal expected) { + mFakeCallback.mConfigurationInternal.assertHasBeenSet(); + assertEquals(expected, mFakeCallback.mConfigurationInternal.getLatest()); mFakeCallback.commitAllChanges(); // Also confirm the listener triggered. - mMockStrategyListener.verifyOnConfigurationChangedCalled(); - mMockStrategyListener.reset(); + mMockConfigChangeListener.verifyOnChangeCalled(); + mMockConfigChangeListener.reset(); return this; } @@ -1410,10 +1145,10 @@ public class TimeZoneDetectorStrategyImplTest { * {@link TimeZoneConfiguration} have been changed. */ Script verifyConfigurationNotChanged() { - mFakeCallback.mConfiguration.assertHasNotBeenSet(); + mFakeCallback.mConfigurationInternal.assertHasNotBeenSet(); // Also confirm the listener did not trigger. - mMockStrategyListener.verifyOnConfigurationChangedNotCalled(); + mMockConfigChangeListener.verifyOnChangeNotCalled(); return this; } @@ -1448,24 +1183,24 @@ public class TimeZoneDetectorStrategyImplTest { return new TelephonyTestCase(matchType, quality, expectedScore); } - private static class MockStrategyListener implements TimeZoneDetectorStrategy.StrategyListener { - private boolean mOnConfigurationChangedCalled; + private static class MockConfigChangeListener implements ConfigurationChangeListener { + private boolean mOnChangeCalled; @Override - public void onConfigurationChanged() { - mOnConfigurationChangedCalled = true; + public void onChange() { + mOnChangeCalled = true; } - void verifyOnConfigurationChangedCalled() { - assertTrue(mOnConfigurationChangedCalled); + void verifyOnChangeCalled() { + assertTrue(mOnChangeCalled); } - void verifyOnConfigurationChangedNotCalled() { - assertFalse(mOnConfigurationChangedCalled); + void verifyOnChangeNotCalled() { + assertFalse(mOnChangeCalled); } void reset() { - mOnConfigurationChangedCalled = false; + mOnChangeCalled = false; } } 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 6358c0dba471..59f0a7987bda 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -1714,6 +1714,27 @@ public class ActivityRecordTests extends WindowTestsBase { } + @Test + public void testProcessInfoUpdateWhenSetState() { + spyOn(mActivity.app); + verifyProcessInfoUpdate(RESUMED, true /* shouldUpdate */, true /* activityChange */); + verifyProcessInfoUpdate(PAUSED, false /* shouldUpdate */, false /* activityChange */); + verifyProcessInfoUpdate(STOPPED, false /* shouldUpdate */, false /* activityChange */); + verifyProcessInfoUpdate(STARTED, true /* shouldUpdate */, true /* activityChange */); + + mActivity.app.removeActivity(mActivity, true /* keepAssociation */); + verifyProcessInfoUpdate(DESTROYING, true /* shouldUpdate */, false /* activityChange */); + verifyProcessInfoUpdate(DESTROYED, true /* shouldUpdate */, false /* activityChange */); + } + + private void verifyProcessInfoUpdate(ActivityState state, boolean shouldUpdate, + boolean activityChange) { + reset(mActivity.app); + mActivity.setState(state, "test"); + verify(mActivity.app, times(shouldUpdate ? 1 : 0)).updateProcessInfo(anyBoolean(), + eq(activityChange), anyBoolean(), anyBoolean()); + } + /** * Creates an activity on display. For non-default display request it will also create a new * display with custom DisplayInfo. diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index d54b4a0a72f6..f8baf8497069 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1110,7 +1110,7 @@ public class DisplayContentTests extends WindowTestsBase { performLayout(mDisplayContent); // The frame is empty because the requested height is zero. - assertTrue(win.getFrameLw().isEmpty()); + assertTrue(win.getFrame().isEmpty()); // The window should be scheduled to resize then the client may report a new non-empty size. win.updateResizingWindowIfNeeded(); assertThat(mWm.mResizingWindows).contains(win); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 0675c6d04422..2d834ac57f51 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -114,7 +114,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow = spy(createWindow(null, TYPE_APPLICATION, "window")); // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from // changing those frames. - doNothing().when(mWindow).computeFrameLw(); + doNothing().when(mWindow).computeFrame(); final WindowManager.LayoutParams attrs = mWindow.mAttrs; attrs.width = MATCH_PARENT; @@ -179,7 +179,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel"); win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES}; - win.getFrameLw().set(0, 0, 500, 100); + win.getFrame().set(0, 0, 500, 100); addWindow(win); InsetsStateController controller = mDisplayContent.getInsetsStateController(); @@ -207,7 +207,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one. WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar"); win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR}; - win.getFrameLw().set(0, 0, 500, 100); + win.getFrame().set(0, 0, 500, 100); addWindow(win); mDisplayContent.getInsetsStateController().onPostLayout(); @@ -232,7 +232,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { WindowState win1 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel1"); win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR}; win1.mAttrs.gravity = Gravity.TOP; - win1.getFrameLw().set(0, 0, 200, 500); + win1.getFrame().set(0, 0, 200, 500); addWindow(win1); assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_TOP); @@ -241,7 +241,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { WindowState win2 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel2"); win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR}; win2.mAttrs.gravity = Gravity.BOTTOM; - win2.getFrameLw().set(0, 0, 200, 500); + win2.getFrame().set(0, 0, 200, 500); addWindow(win2); assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_BOTTOM); @@ -250,7 +250,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { WindowState win3 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel3"); win3.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR}; win3.mAttrs.gravity = Gravity.LEFT; - win3.getFrameLw().set(0, 0, 200, 500); + win3.getFrame().set(0, 0, 200, 500); addWindow(win3); assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_LEFT); @@ -259,7 +259,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { WindowState win4 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel4"); win4.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR}; win4.mAttrs.gravity = Gravity.RIGHT; - win4.getFrameLw().set(0, 0, 200, 500); + win4.getFrame().set(0, 0, 200, 500); addWindow(win4); assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_RIGHT); @@ -274,11 +274,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0); + assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0); assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); - assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); } @@ -290,11 +290,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); } @@ -306,11 +306,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); } @@ -322,11 +322,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0); + assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0); assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); - assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); } @@ -342,11 +342,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); } @@ -362,11 +362,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0); + assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0); assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); } @@ -381,11 +381,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0); + assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0); assertInsetByTopBottom(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0); - assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); } @@ -402,10 +402,10 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0); + assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0); } @Test @@ -422,10 +422,10 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0); + assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0); } @Test @@ -442,10 +442,10 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); - assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getContentFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0); } @Test @@ -462,10 +462,10 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); - assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getContentFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0); } @Test @@ -484,10 +484,10 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0); + assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0); } @Test @@ -506,10 +506,10 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0); + assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0); } @Test @@ -529,10 +529,10 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0); + assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0); } @@ -549,11 +549,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); - assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mWindow.getContentFrameLw(), + assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getContentFrame(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); } @Test @@ -570,11 +570,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); - assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0); - assertInsetBy(mWindow.getContentFrameLw(), + assertInsetBy(mWindow.getStableFrame(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0); + assertInsetBy(mWindow.getContentFrame(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0); assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); + assertInsetBy(mWindow.getDisplayFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); } @Test @@ -594,8 +594,8 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); - assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mWindow.getContentFrameLw(), + assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getContentFrame(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); } @@ -615,7 +615,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); } @Test @@ -636,8 +636,8 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mWindow.getContentFrameLw(), + assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getContentFrame(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); } @@ -655,11 +655,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); - assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); - assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0, + assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getContentFrame(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); } @Test @@ -676,12 +676,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); - assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, + assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); - assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0, + assertInsetBy(mWindow.getContentFrame(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); } @Test @@ -698,11 +698,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); - assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); - assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0, + assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getContentFrame(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); } @Test @@ -719,11 +719,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); - assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0, + assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getContentFrame(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0); } @Test @@ -740,11 +740,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0); } @Test @@ -904,34 +904,34 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { updateDecorWindow(decorWindow, MATCH_PARENT, DECOR_WINDOW_INSET, TOP); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - assertInsetByTopBottom(mWindow.getContentFrameLw(), DECOR_WINDOW_INSET, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), DECOR_WINDOW_INSET, NAV_BAR_HEIGHT); // Decor on bottom updateDecorWindow(decorWindow, MATCH_PARENT, DECOR_WINDOW_INSET, BOTTOM); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, DECOR_WINDOW_INSET); // Decor on the left updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, MATCH_PARENT, LEFT); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - assertInsetBy(mWindow.getContentFrameLw(), DECOR_WINDOW_INSET, STATUS_BAR_HEIGHT, 0, + assertInsetBy(mWindow.getContentFrame(), DECOR_WINDOW_INSET, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); // Decor on the right updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, MATCH_PARENT, RIGHT); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, DECOR_WINDOW_INSET, + assertInsetBy(mWindow.getContentFrame(), 0, STATUS_BAR_HEIGHT, DECOR_WINDOW_INSET, NAV_BAR_HEIGHT); // Decor not allowed as inset updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, DECOR_WINDOW_INSET, TOP); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); } private void updateDecorWindow(WindowState decorWindow, int width, int height, int gravity) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 4483f8c341cf..b50530ed3059 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -297,7 +297,7 @@ public class DisplayPolicyTests extends WindowTestsBase { final WindowState navigationBar = createNavigationBarWindow(); - navigationBar.getFrameLw().set(new Rect(100, 200, 200, 300)); + navigationBar.getFrame().set(new Rect(100, 200, 200, 300)); assertFalse("Freeform is overlapping with navigation bar", DisplayPolicy.isOverlappingWithNavBar(targetWin, navigationBar)); @@ -377,7 +377,7 @@ public class DisplayPolicyTests extends WindowTestsBase { mDisplayContent.setInputMethodWindowLocked(mImeWindow); mImeWindow.mAttrs.setFitInsetsSides(Side.all() & ~Side.BOTTOM); - mImeWindow.getGivenContentInsetsLw().set(0, displayInfo.logicalHeight, 0, 0); + mImeWindow.mGivenContentInsets.set(0, displayInfo.logicalHeight, 0, 0); mImeWindow.getControllableInsetProvider().setServerVisible(true); displayPolicy.beginLayoutLw(mDisplayContent.mDisplayFrames, 0 /* UI mode */); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java index 1d6dd0b566a1..a0fa9369e7a8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -61,7 +61,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testPostLayout() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); - statusBar.getFrameLw().set(0, 0, 500, 100); + statusBar.getFrame().set(0, 0, 500, 100); statusBar.mHasSurface = true; mProvider.setWindow(statusBar, null, null); mProvider.onPostLayout(); @@ -76,9 +76,9 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testPostLayout_givenInsets() { final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime"); - ime.getFrameLw().set(0, 0, 500, 100); - ime.getGivenContentInsetsLw().set(0, 0, 0, 60); - ime.getGivenVisibleInsetsLw().set(0, 0, 0, 75); + ime.getFrame().set(0, 0, 500, 100); + ime.mGivenContentInsets.set(0, 0, 0, 60); + ime.mGivenVisibleInsets.set(0, 0, 0, 75); ime.mHasSurface = true; mProvider.setWindow(ime, null, null); mProvider.onPostLayout(); @@ -94,7 +94,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testPostLayout_invisible() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); - statusBar.getFrameLw().set(0, 0, 500, 100); + statusBar.getFrame().set(0, 0, 500, 100); mProvider.setWindow(statusBar, null, null); mProvider.onPostLayout(); assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500), @@ -104,7 +104,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testPostLayout_frameProvider() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); - statusBar.getFrameLw().set(0, 0, 500, 100); + statusBar.getFrame().set(0, 0, 500, 100); statusBar.mHasSurface = true; mProvider.setWindow(statusBar, (displayFrames, windowState, rect) -> { @@ -118,7 +118,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { public void testUpdateControlForTarget() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); - statusBar.getFrameLw().set(0, 0, 500, 100); + statusBar.getFrame().set(0, 0, 500, 100); // We must not have control or control target before we have the insets source window. mProvider.updateControlForTarget(target, true /* force */); @@ -163,7 +163,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { public void testUpdateControlForFakeTarget() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); - statusBar.getFrameLw().set(0, 0, 500, 100); + statusBar.getFrame().set(0, 0, 500, 100); mProvider.setWindow(statusBar, null, null); mProvider.updateControlForFakeTarget(target); assertNotNull(mProvider.getControl(target)); @@ -176,7 +176,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { public void testUpdateSourceFrameForIme() { final WindowState inputMethod = createWindow(null, TYPE_INPUT_METHOD, "inputMethod"); - inputMethod.getFrameLw().set(new Rect(0, 400, 500, 500)); + inputMethod.getFrame().set(new Rect(0, 400, 500, 500)); mImeProvider.setWindow(inputMethod, null, null); mImeProvider.setServerVisible(false); @@ -190,7 +190,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { mImeProvider.setServerVisible(true); mImeSource.setVisible(true); mImeProvider.updateSourceFrame(); - assertEquals(inputMethod.getFrameLw(), mImeSource.getFrame()); + assertEquals(inputMethod.getFrame(), mImeSource.getFrame()); insets = mImeSource.calculateInsets(new Rect(0, 0, 500, 500), false /* ignoreVisibility */); assertEquals(Insets.of(0, 0, 0, 100), insets); @@ -200,7 +200,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { public void testInsetsModified() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); - statusBar.getFrameLw().set(0, 0, 500, 100); + statusBar.getFrame().set(0, 0, 500, 100); mProvider.setWindow(statusBar, null, null); mProvider.updateControlForTarget(target, false /* force */); InsetsState state = new InsetsState(); @@ -213,7 +213,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { public void testInsetsModified_noControl() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); - statusBar.getFrameLw().set(0, 0, 500, 100); + statusBar.getFrame().set(0, 0, 500, 100); mProvider.setWindow(statusBar, null, null); InsetsState state = new InsetsState(); state.getSource(ITYPE_STATUS_BAR).setVisible(false); @@ -224,7 +224,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testInsetGeometries() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); - statusBar.getFrameLw().set(0, 0, 500, 100); + statusBar.getFrame().set(0, 0, 500, 100); statusBar.mHasSurface = true; mProvider.setWindow(statusBar, null, null); mProvider.onPostLayout(); @@ -236,7 +236,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { false /* ignoreVisibility */)); // Don't apply left insets if window is left-of inset-window but still overlaps - statusBar.getFrameLw().set(100, 0, 0, 0); + statusBar.getFrame().set(100, 0, 0, 0); assertEquals(Insets.of(0, 0, 0, 0), mProvider.getSource().calculateInsets(new Rect(-100, 0, 400, 500), false /* ignoreVisibility */)); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java index bce1142c99be..ca3f815698e8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java @@ -53,7 +53,6 @@ import androidx.test.filters.FlakyTest; import androidx.test.filters.MediumTest; import com.android.compatibility.common.util.SystemUtil; -import com.android.internal.annotations.GuardedBy; import org.junit.After; import org.junit.Before; @@ -76,14 +75,10 @@ public class TaskStackChangedListenerTest { private static final int WAIT_TIMEOUT_MS = 5000; private static final Object sLock = new Object(); - @GuardedBy("sLock") - private static boolean sTaskStackChangedCalled; - private static boolean sActivityBResumed; @Before public void setUp() throws Exception { mService = ActivityManager.getService(); - sTaskStackChangedCalled = false; } @After @@ -94,47 +89,33 @@ public class TaskStackChangedListenerTest { @Test @Presubmit - @FlakyTest(bugId = 130388819) public void testTaskStackChanged_afterFinish() throws Exception { + final TestActivity activity = startTestActivity(ActivityA.class); + final CountDownLatch latch = new CountDownLatch(1); registerTaskStackChangedListener(new TaskStackListener() { @Override public void onTaskStackChanged() throws RemoteException { - synchronized (sLock) { - sTaskStackChangedCalled = true; - } + latch.countDown(); } }); - Context context = getInstrumentation().getContext(); - context.startActivity( - new Intent(context, ActivityA.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); - UiDevice.getInstance(getInstrumentation()).waitForIdle(); - synchronized (sLock) { - assertTrue(sTaskStackChangedCalled); - } - assertTrue(sActivityBResumed); + activity.finish(); + waitForCallback(latch); } @Test @Presubmit public void testTaskStackChanged_resumeWhilePausing() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); registerTaskStackChangedListener(new TaskStackListener() { @Override public void onTaskStackChanged() throws RemoteException { - synchronized (sLock) { - sTaskStackChangedCalled = true; - } + latch.countDown(); } }); - final Context context = getInstrumentation().getContext(); - context.startActivity(new Intent(context, ResumeWhilePausingActivity.class).addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK)); - UiDevice.getInstance(getInstrumentation()).waitForIdle(); - - synchronized (sLock) { - assertTrue(sTaskStackChangedCalled); - } + startTestActivity(ResumeWhilePausingActivity.class); + waitForCallback(latch); } @Test @@ -512,7 +493,7 @@ public class TaskStackChangedListenerTest { try { final boolean result = latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); if (!result) { - throw new RuntimeException("Timed out waiting for task stack change notification"); + throw new AssertionError("Timed out waiting for task stack change notification"); } } catch (InterruptedException e) { } @@ -569,19 +550,6 @@ public class TaskStackChangedListenerTest { } public static class ActivityA extends TestActivity { - - private boolean mActivityBLaunched = false; - - @Override - protected void onPostResume() { - super.onPostResume(); - if (mActivityBLaunched) { - return; - } - mActivityBLaunched = true; - finish(); - startActivity(new Intent(this, ActivityB.class)); - } } public static class ActivityB extends TestActivity { @@ -589,10 +557,6 @@ public class TaskStackChangedListenerTest { @Override protected void onPostResume() { super.onPostResume(); - synchronized (sLock) { - sTaskStackChangedCalled = false; - } - sActivityBResumed = true; finish(); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index 4a8e8dafb57d..dc859046db42 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -86,11 +86,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public int getMaxWallpaperLayer() { - return 0; - } - - @Override public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) { return attrs.type == TYPE_NOTIFICATION_SHADE; } @@ -377,11 +372,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public boolean isTopLevelWindow(int windowType) { - return false; - } - - @Override public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { } diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java index ed9e2707ae39..63367ac2badf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -133,8 +133,8 @@ public class WallpaperControllerTests extends WindowTestsBase { int expectedWidth = (int) (wallpaperWidth * (displayHeight / (double) wallpaperHeight)); // Check that the wallpaper is correctly scaled - assertEquals(new Rect(0, 0, expectedWidth, displayHeight), wallpaperWindow.getFrameLw()); - Rect portraitFrame = wallpaperWindow.getFrameLw(); + assertEquals(new Rect(0, 0, expectedWidth, displayHeight), wallpaperWindow.getFrame()); + Rect portraitFrame = wallpaperWindow.getFrame(); // Rotate the display dc.getDisplayRotation().updateOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, true); @@ -149,7 +149,7 @@ public class WallpaperControllerTests extends WindowTestsBase { // Check that the wallpaper has the same frame in landscape than in portrait assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getConfiguration().orientation); - assertEquals(portraitFrame, wallpaperWindow.getFrameLw()); + assertEquals(portraitFrame, wallpaperWindow.getFrame()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java index eb2aa41192c2..ca3626d09062 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java @@ -89,29 +89,29 @@ public class WindowFrameTests extends WindowTestsBase { } private void assertFrame(WindowState w, Rect frame) { - assertEquals(w.getFrameLw(), frame); + assertEquals(w.getFrame(), frame); } private void assertFrame(WindowState w, int left, int top, int right, int bottom) { - assertRect(w.getFrameLw(), left, top, right, bottom); + assertRect(w.getFrame(), left, top, right, bottom); } private void assertRelFrame(WindowState w, int left, int top, int right, int bottom) { - assertRect(w.getRelativeFrameLw(), left, top, right, bottom); + assertRect(w.getRelativeFrame(), left, top, right, bottom); } private void assertContentFrame(WindowState w, Rect expectedRect) { - assertRect(w.getContentFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right, + assertRect(w.getContentFrame(), expectedRect.left, expectedRect.top, expectedRect.right, expectedRect.bottom); } private void assertVisibleFrame(WindowState w, Rect expectedRect) { - assertRect(w.getVisibleFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right, + assertRect(w.getVisibleFrame(), expectedRect.left, expectedRect.top, expectedRect.right, expectedRect.bottom); } private void assertStableFrame(WindowState w, Rect expectedRect) { - assertRect(w.getStableFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right, + assertRect(w.getStableFrame(), expectedRect.left, expectedRect.top, expectedRect.right, expectedRect.bottom); } @@ -155,7 +155,7 @@ public class WindowFrameTests extends WindowTestsBase { // the difference between mFrame and ContentFrame. Visible // and stable frames work the same way. w.getWindowFrames().setFrames(pf, df, cf, vf, dcf, sf); - w.computeFrameLw(); + w.computeFrame(); assertFrame(w, 0, 0, 1000, 1000); assertRelFrame(w, 0, 0, 1000, 1000); assertContentInset(w, 0, topContentInset, 0, bottomContentInset); @@ -170,14 +170,14 @@ public class WindowFrameTests extends WindowTestsBase { w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT w.mRequestedWidth = 100; w.mRequestedHeight = 100; - w.computeFrameLw(); + w.computeFrame(); assertFrame(w, 100, 100, 200, 200); assertRelFrame(w, 100, 100, 200, 200); assertContentInset(w, 0, 0, 0, 0); // In this case the frames are shrunk to the window frame. - assertContentFrame(w, w.getFrameLw()); - assertVisibleFrame(w, w.getFrameLw()); - assertStableFrame(w, w.getFrameLw()); + assertContentFrame(w, w.getFrame()); + assertVisibleFrame(w, w.getFrame()); + assertStableFrame(w, w.getFrame()); } @Test @@ -193,7 +193,7 @@ public class WindowFrameTests extends WindowTestsBase { // Here the window has FILL_PARENT, FILL_PARENT // so we expect it to fill the entire available frame. w.getWindowFrames().setFrames(pf, pf, pf, pf, pf, pf); - w.computeFrameLw(); + w.computeFrame(); assertFrame(w, 0, 0, 1000, 1000); assertRelFrame(w, 0, 0, 1000, 1000); @@ -202,14 +202,14 @@ public class WindowFrameTests extends WindowTestsBase { // and we use mRequestedWidth/mRequestedHeight w.mAttrs.width = 300; w.mAttrs.height = 300; - w.computeFrameLw(); + w.computeFrame(); // Explicit width and height without requested width/height // gets us nothing. assertFrame(w, 0, 0, 0, 0); w.mRequestedWidth = 300; w.mRequestedHeight = 300; - w.computeFrameLw(); + w.computeFrame(); // With requestedWidth/Height we can freely choose our size within the // parent bounds. assertFrame(w, 0, 0, 300, 300); @@ -222,14 +222,14 @@ public class WindowFrameTests extends WindowTestsBase { w.mRequestedWidth = -1; w.mAttrs.width = 100; w.mAttrs.height = 100; - w.computeFrameLw(); + w.computeFrame(); assertFrame(w, 0, 0, 100, 100); w.mAttrs.flags = 0; // But sizes too large will be clipped to the containing frame w.mRequestedWidth = 1200; w.mRequestedHeight = 1200; - w.computeFrameLw(); + w.computeFrame(); assertFrame(w, 0, 0, 1000, 1000); // Before they are clipped though windows will be shifted @@ -237,7 +237,7 @@ public class WindowFrameTests extends WindowTestsBase { w.mAttrs.y = 300; w.mRequestedWidth = 1000; w.mRequestedHeight = 1000; - w.computeFrameLw(); + w.computeFrame(); assertFrame(w, 0, 0, 1000, 1000); // If there is room to move around in the parent frame the window will be shifted according @@ -247,18 +247,18 @@ public class WindowFrameTests extends WindowTestsBase { w.mRequestedWidth = 300; w.mRequestedHeight = 300; w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP; - w.computeFrameLw(); + w.computeFrame(); assertFrame(w, 700, 0, 1000, 300); assertRelFrame(w, 700, 0, 1000, 300); w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM; - w.computeFrameLw(); + w.computeFrame(); assertFrame(w, 700, 700, 1000, 1000); assertRelFrame(w, 700, 700, 1000, 1000); // Window specified x and y are interpreted as offsets in the opposite // direction of gravity w.mAttrs.x = 100; w.mAttrs.y = 100; - w.computeFrameLw(); + w.computeFrame(); assertFrame(w, 600, 600, 900, 900); assertRelFrame(w, 600, 600, 900, 900); } @@ -285,12 +285,12 @@ public class WindowFrameTests extends WindowTestsBase { final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); final WindowFrames windowFrames = w.getWindowFrames(); windowFrames.setFrames(pf, pf, pf, pf, pf, pf); - w.computeFrameLw(); + w.computeFrame(); // For non fullscreen tasks the containing frame is based off the // task bounds not the parent frame. - assertEquals(resolvedTaskBounds, w.getFrameLw()); - assertEquals(0, w.getRelativeFrameLw().left); - assertEquals(0, w.getRelativeFrameLw().top); + assertEquals(resolvedTaskBounds, w.getFrame()); + assertEquals(0, w.getRelativeFrame().left); + assertEquals(0, w.getRelativeFrame().top); assertContentFrame(w, resolvedTaskBounds); assertContentInset(w, 0, 0, 0, 0); @@ -300,10 +300,10 @@ public class WindowFrameTests extends WindowTestsBase { final int cfBottom = logicalHeight / 2; final Rect cf = new Rect(0, 0, cfRight, cfBottom); windowFrames.setFrames(pf, pf, cf, cf, pf, cf); - w.computeFrameLw(); - assertEquals(resolvedTaskBounds, w.getFrameLw()); - assertEquals(0, w.getRelativeFrameLw().left); - assertEquals(0, w.getRelativeFrameLw().top); + w.computeFrame(); + assertEquals(resolvedTaskBounds, w.getFrame()); + assertEquals(0, w.getRelativeFrame().left); + assertEquals(0, w.getRelativeFrame().top); int contentInsetRight = resolvedTaskBounds.right - cfRight; int contentInsetBottom = resolvedTaskBounds.bottom - cfBottom; assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom); @@ -334,12 +334,12 @@ public class WindowFrameTests extends WindowTestsBase { final WindowFrames windowFrames = w.getWindowFrames(); windowFrames.setFrames(pf, df, cf, vf, dcf, sf); - w.computeFrameLw(); + w.computeFrame(); assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom); windowFrames.mDecorFrame.setEmpty(); // Likewise with no decor frame we would get no crop - w.computeFrameLw(); + w.computeFrame(); assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight); // Now we set up a window which doesn't fill the entire decor frame. @@ -353,7 +353,7 @@ public class WindowFrameTests extends WindowTestsBase { w.mAttrs.height = logicalHeight / 2; w.mRequestedWidth = logicalWidth / 2; w.mRequestedHeight = logicalHeight / 2; - w.computeFrameLw(); + w.computeFrame(); // Normally the crop is shrunk from the decor frame // to the computed window frame. @@ -390,7 +390,7 @@ public class WindowFrameTests extends WindowTestsBase { final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); final WindowFrames windowFrames = w.getWindowFrames(); windowFrames.setFrames(pf, pf, pf, pf, pf, pf); - w.computeFrameLw(); + w.computeFrame(); // For non fullscreen tasks the containing frame is based off the // task bounds not the parent frame. assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); @@ -408,7 +408,7 @@ public class WindowFrameTests extends WindowTestsBase { task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); task.setBounds(null); windowFrames.setFrames(pf, pf, cf, cf, pf, cf); - w.computeFrameLw(); + w.computeFrame(); assertFrame(w, cf); assertContentFrame(w, cf); assertContentInset(w, 0, 0, 0, 0); @@ -430,7 +430,7 @@ public class WindowFrameTests extends WindowTestsBase { final WindowFrames windowFrames = w.getWindowFrames(); windowFrames.setFrames(pf, pf, pf, pf, pf, pf); windowFrames.setDisplayCutout(cutout); - w.computeFrameLw(); + w.computeFrame(); assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50); assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0); @@ -469,20 +469,20 @@ public class WindowFrameTests extends WindowTestsBase { task.setBounds(winRect); w.getWindowFrames().setFrames(pf, df, cf, vf, dcf, sf); - w.computeFrameLw(); + w.computeFrame(); final Rect expected = new Rect(winRect.left, cf.bottom - winRect.height(), winRect.right, cf.bottom); - assertEquals(expected, w.getFrameLw()); - assertEquals(expected, w.getContentFrameLw()); - assertEquals(expected, w.getVisibleFrameLw()); + assertEquals(expected, w.getFrame()); + assertEquals(expected, w.getContentFrame()); + assertEquals(expected, w.getVisibleFrame()); // Now check that it won't get moved beyond the top and then has appropriate insets winRect.bottom = 600; task.setBounds(winRect); w.setBounds(winRect); w.getWindowFrames().setFrames(pf, df, cf, vf, dcf, sf); - w.computeFrameLw(); + w.computeFrame(); assertFrame(w, winRect.left, 0, winRect.right, winRect.height()); expected.top = 0; @@ -492,8 +492,8 @@ public class WindowFrameTests extends WindowTestsBase { // Check that it's moved back without ime insets w.getWindowFrames().setFrames(pf, df, pf, pf, dcf, sf); - w.computeFrameLw(); - assertEquals(winRect, w.getFrameLw()); + w.computeFrame(); + assertEquals(winRect, w.getFrame()); } private WindowState createWindow() { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 289d54e967f5..46a6a82faba5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -930,23 +930,36 @@ public class WindowOrganizerTests extends WindowTestsBase { final Task stack = createStack(); final Task task = createTask(stack); final ActivityRecord activity = createActivityRecordInTask(stack.mDisplayContent, task); + final Task stack2 = createStack(); + final Task task2 = createTask(stack2); + final ActivityRecord activity2 = createActivityRecordInTask(stack.mDisplayContent, task2); final ITaskOrganizer organizer = registerMockOrganizer(); // Setup the task to be controlled by the MW mode organizer stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); assertTrue(stack.isOrganized()); + assertTrue(stack2.isOrganized()); // Verify a back pressed does not call the organizer mWm.mAtmService.onBackPressedOnTaskRoot(activity.token); verify(organizer, never()).onBackPressedOnTaskRoot(any()); // Enable intercepting back - mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(organizer, - true); + mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( + stack.mRemoteToken.toWindowContainerToken(), true); // Verify now that the back press does call the organizer mWm.mAtmService.onBackPressedOnTaskRoot(activity.token); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); + + // Disable intercepting back + mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( + stack.mRemoteToken.toWindowContainerToken(), false); + + // Verify now that the back press no longer calls the organizer + mWm.mAtmService.onBackPressedOnTaskRoot(activity.token); + verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index f095fd42900b..9603d28c286b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -479,7 +479,7 @@ public class WindowStateTests extends WindowTestsBase { app.mHasSurface = true; app.mSurfaceControl = mock(SurfaceControl.class); try { - app.getFrameLw().set(10, 20, 60, 80); + app.getFrame().set(10, 20, 60, 80); app.updateSurfacePosition(t); app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true); @@ -519,7 +519,7 @@ public class WindowStateTests extends WindowTestsBase { new Rect(95, 378, 105, 400)); wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400))); - app.computeFrameLw(); + app.computeFrame(); assertThat(app.getWmDisplayCutout().getDisplayCutout(), is(cutout.inset(7, 10, 5, 20))); } @@ -633,7 +633,7 @@ public class WindowStateTests extends WindowTestsBase { final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); final DisplayContent dc = createNewDisplay(); - win0.getFrameLw().offsetTo(PARENT_WINDOW_OFFSET, 0); + win0.getFrame().offsetTo(PARENT_WINDOW_OFFSET, 0); dc.reparentDisplayContent(win0, win0.getSurfaceControl()); dc.updateLocation(win0, DISPLAY_IN_PARENT_WINDOW_OFFSET, 0); @@ -644,7 +644,7 @@ public class WindowStateTests extends WindowTestsBase { win1.mHasSurface = true; win1.mSurfaceControl = mock(SurfaceControl.class); win1.mAttrs.surfaceInsets.set(1, 2, 3, 4); - win1.getFrameLw().offsetTo(WINDOW_OFFSET, 0); + win1.getFrame().offsetTo(WINDOW_OFFSET, 0); win1.updateSurfacePosition(t); win1.getTransformationMatrix(values, matrix); 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 f86d8f15353e..38c4e0a7de02 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1131,7 +1131,7 @@ class WindowTestsBase extends SystemServiceTestsBase { } @Override - public boolean isGoneForLayoutLw() { + public boolean isGoneForLayout() { return false; } diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp index 229a9aa65166..f55d4d474bfb 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -26,7 +26,7 @@ android_test_helper_app { java_test_host { name: "StagedInstallInternalTest", srcs: ["src/**/*.java"], - libs: ["tradefed"], + libs: ["tradefed", "cts-shim-host-lib"], static_libs: [ "testng", "compatibility-tradefed", 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 d7b07967f979..702f8719ff24 100644 --- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -16,6 +16,8 @@ package com.android.tests.stagedinstallinternal.host; +import static com.android.cts.shim.lib.ShimPackage.SHIM_APEX_PACKAGE_NAME; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertTrue; @@ -82,7 +84,8 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { Log.e(TAG, e); } deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex", - "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex"); + "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex", + "/data/apex/active/" + SHIM_APEX_PACKAGE_NAME + "*.apex"); } @Before @@ -151,43 +154,78 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { runPhase("testSystemServerRestartDoesNotAffectStagedSessions_Verify"); } + // Test waiting time for staged session to be ready using adb staged install can be altered @Test - public void testAdbStagedInstallWaitForReadyFlagWorks() throws Exception { + public void testAdbStagdReadyTimeoutFlagWorks() throws Exception { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - File apexFile = mTestUtils.getTestFile(SHIM_V2); - String output = getDevice().executeAdbCommand("install", "--staged", - "--wait-for-staged-ready", "60000", apexFile.getAbsolutePath()); + final File apexFile = mTestUtils.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"); - String sessionId = getDevice().executeShellCommand( + final String sessionId = getDevice().executeShellCommand( "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim(); assertThat(sessionId).isNotEmpty(); } + // Test adb staged installation wait for session to be ready by default @Test - public void testAdbStagedInstallNoWaitFlagWorks() throws Exception { + public void testAdbStagedInstallWaitsTillReadyByDefault() throws Exception { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - File apexFile = mTestUtils.getTestFile(SHIM_V2); - String output = getDevice().executeAdbCommand("install", "--staged", - "--no-wait", apexFile.getAbsolutePath()); + final File apexFile = mTestUtils.getTestFile(SHIM_V2); + final String output = getDevice().executeAdbCommand("install", "--staged", + apexFile.getAbsolutePath()); + assertThat(output).contains("Reboot device to apply staged session"); + final String sessionId = getDevice().executeShellCommand( + "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim(); + assertThat(sessionId).isNotEmpty(); + } + + // Test we can skip waiting for staged session to be ready + @Test + public void testAdbStagedReadyWaitCanBeSkipped() throws Exception { + assumeTrue("Device does not support updating APEX", + mHostUtils.isApexUpdateSupported()); + + final File apexFile = mTestUtils.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"); assertThat(output).contains("Success"); - String sessionId = getDevice().executeShellCommand( + final String sessionId = getDevice().executeShellCommand( "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim(); assertThat(sessionId).isEmpty(); } + // Test rollback-app command waits for staged sessions to be ready + @Test + public void testAdbRollbackAppWaitsForStagedReady() throws Exception { + assumeTrue("Device does not support updating APEX", + mHostUtils.isApexUpdateSupported()); + + final File apexFile = mTestUtils.getTestFile(SHIM_V2); + String output = getDevice().executeAdbCommand("install", "--staged", + "--enable-rollback", apexFile.getAbsolutePath()); + assertThat(output).contains("Reboot device to apply staged session"); + getDevice().reboot(); + output = getDevice().executeShellCommand("pm rollback-app " + SHIM_APEX_PACKAGE_NAME); + assertThat(output).contains("Reboot device to apply staged session"); + final String sessionId = getDevice().executeShellCommand( + "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim(); + assertThat(sessionId).isNotEmpty(); + } + @Test public void testAdbInstallMultiPackageCommandWorks() throws Exception { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - File apexFile = mTestUtils.getTestFile(SHIM_V2); - File apkFile = mTestUtils.getTestFile(APK_A); - String output = getDevice().executeAdbCommand("install-multi-package", + final File apexFile = mTestUtils.getTestFile(SHIM_V2); + final File apkFile = mTestUtils.getTestFile(APK_A); + final String output = getDevice().executeAdbCommand("install-multi-package", apexFile.getAbsolutePath(), apkFile.getAbsolutePath()); assertThat(output).contains("Created parent session"); assertThat(output).contains("Created child session"); @@ -227,16 +265,16 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { private void restartSystemServer() throws Exception { // Restart the system server - ProcessInfo oldPs = getDevice().getProcessByName("system_server"); + final ProcessInfo oldPs = getDevice().getProcessByName("system_server"); getDevice().enableAdbRoot(); // Need root to restart system server assertThat(getDevice().executeShellCommand("am restart")).contains("Restart the system"); getDevice().disableAdbRoot(); // Wait for new system server process to start - long start = System.currentTimeMillis(); + final long start = System.currentTimeMillis(); while (System.currentTimeMillis() < start + SYSTEM_SERVER_TIMEOUT_MS) { - ProcessInfo newPs = getDevice().getProcessByName("system_server"); + final ProcessInfo newPs = getDevice().getProcessByName("system_server"); if (newPs != null) { if (newPs.getPid() != oldPs.getPid()) { getDevice().waitForDeviceAvailable(); diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index a3673df1c713..bcb5c992f162 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -1199,7 +1199,7 @@ public class ConnectivityServiceTest { MockitoAnnotations.initMocks(this); when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics); - when(mUserManager.getUsers(eq(true))).thenReturn( + when(mUserManager.getAliveUsers()).thenReturn( Arrays.asList(new UserInfo[] { new UserInfo(VPN_USER, "", 0), })); diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java index 5a29c2c96ba7..de35f910d53a 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -123,7 +123,7 @@ public class PermissionMonitorTest { MockitoAnnotations.initMocks(this); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager); - when(mUserManager.getUsers(eq(true))).thenReturn( + when(mUserManager.getAliveUsers()).thenReturn( Arrays.asList(new UserInfo[] { new UserInfo(MOCK_USER1, "", 0), new UserInfo(MOCK_USER2, "", 0), diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index e8c4ee9c628d..c76b4cd501e7 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -1238,15 +1238,14 @@ public class VpnTest { * @see UserManagerService#getUsers(boolean) */ doAnswer(invocation -> { - final boolean excludeDying = (boolean) invocation.getArguments()[0]; final ArrayList<UserInfo> result = new ArrayList<>(users.length); for (UserInfo ui : users) { - if (!excludeDying || (ui.isEnabled() && !ui.partial)) { + if (ui.isEnabled() && !ui.partial) { result.add(ui); } } return result; - }).when(mUserManager).getUsers(anyBoolean()); + }).when(mUserManager).getAliveUsers(); doAnswer(invocation -> { final int id = (int) invocation.getArguments()[0]; diff --git a/tools/preload-check/Android.bp b/tools/preload-check/Android.bp index 87b31d22af32..aaa6d76b39e3 100644 --- a/tools/preload-check/Android.bp +++ b/tools/preload-check/Android.bp @@ -15,7 +15,7 @@ java_test_host { name: "PreloadCheck", srcs: ["src/**/*.java"], - java_resources: [":preloaded-classes-blacklist"], + java_resources: [":preloaded-classes-denylist"], libs: ["tradefed"], test_suites: ["general-tests"], required: ["preload-check-device"], diff --git a/tools/preload-check/src/com/android/preload/check/PreloadCheck.java b/tools/preload-check/src/com/android/preload/check/PreloadCheck.java index 00fd414e3ee2..3d851531d7e7 100644 --- a/tools/preload-check/src/com/android/preload/check/PreloadCheck.java +++ b/tools/preload-check/src/com/android/preload/check/PreloadCheck.java @@ -69,13 +69,13 @@ public class PreloadCheck implements IDeviceTest { } /** - * Test the classes mentioned in the embedded preloaded-classes blacklist. + * Test the classes mentioned in the embedded preloaded-classes denylist. */ @Test - public void testBlackList() throws Exception { + public void testDenyList() throws Exception { StringBuilder sb = new StringBuilder(); try (BufferedReader br = new BufferedReader(new InputStreamReader(getClass() - .getResourceAsStream("/preloaded-classes-blacklist")))) { + .getResourceAsStream("/preloaded-classes-denylist")))) { String s; while ((s = br.readLine()) != null) { s = s.trim(); |