diff options
103 files changed, 2117 insertions, 698 deletions
diff --git a/Android.bp b/Android.bp index a1061681358f..1eba3e2711f1 100644 --- a/Android.bp +++ b/Android.bp @@ -953,6 +953,7 @@ stubs_defaults { "test-base/src/**/*.java", ":opt-telephony-srcs", ":opt-net-voip-srcs", + ":core-current-stubs-source", ":core_public_api_files", ":updatable-media-srcs", "test-mock/src/**/*.java", @@ -1017,6 +1018,7 @@ stubs_defaults { "core/java/**/*.logtags", ":opt-telephony-srcs", ":opt-net-voip-srcs", + ":core-current-stubs-source", ":core_public_api_files", ":updatable-media-srcs", ":jobscheduler-framework-source", @@ -1570,3 +1572,14 @@ filegroup { ], } +// Avoid including Parcelable classes as we don't want to have two copies of +// Parcelable cross the process. +filegroup { + name: "framework-cellbroadcast-shared-srcs", + srcs: [ + "core/java/android/util/LocalLog.java", + "core/java/android/util/Slog.java", + "core/java/com/android/internal/util/State.java", + "core/java/com/android/internal/util/StateMachine.java", + ], +}
\ No newline at end of file diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index fe7b4aecb792..7c472a9813aa 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -1851,16 +1851,6 @@ public class AppStandbyController implements AppStandbyInternal { * Observe settings changes for {@link Global#APP_IDLE_CONSTANTS}. */ private class SettingsObserver extends ContentObserver { - /** - * This flag has been used to disable app idle on older builds with bug b/26355386. - */ - @Deprecated - private static final String KEY_IDLE_DURATION_OLD = "idle_duration"; - @Deprecated - private static final String KEY_IDLE_DURATION = "idle_duration2"; - @Deprecated - private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold"; - private static final String KEY_PAROLE_INTERVAL = "parole_interval"; private static final String KEY_PAROLE_WINDOW = "parole_window"; private static final String KEY_PAROLE_DURATION = "parole_duration"; @@ -1995,7 +1985,7 @@ public class AppStandbyController implements AppStandbyInternal { : DEFAULT_EXEMPTED_SYNC_START_TIMEOUT); mUnexemptedSyncScheduledTimeoutMillis = mParser.getDurationMillis( - KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION, + KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION, COMPRESS_TIME ? ONE_MINUTE : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); // TODO diff --git a/api/current.txt b/api/current.txt index 8733aab02b00..dda327f19f41 100644 --- a/api/current.txt +++ b/api/current.txt @@ -25387,6 +25387,7 @@ package android.media { method public android.graphics.Bitmap getFrameAtIndex(int, @NonNull android.media.MediaMetadataRetriever.BitmapParams); method public android.graphics.Bitmap getFrameAtIndex(int); method public android.graphics.Bitmap getFrameAtTime(long, int); + method public android.graphics.Bitmap getFrameAtTime(long, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams); method public android.graphics.Bitmap getFrameAtTime(long); method public android.graphics.Bitmap getFrameAtTime(); method @NonNull public java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams); @@ -25396,6 +25397,7 @@ package android.media { method public android.graphics.Bitmap getPrimaryImage(@NonNull android.media.MediaMetadataRetriever.BitmapParams); method public android.graphics.Bitmap getPrimaryImage(); method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int); + method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams); method public void release(); method public void setDataSource(String) throws java.lang.IllegalArgumentException; method public void setDataSource(String, java.util.Map<java.lang.String,java.lang.String>) throws java.lang.IllegalArgumentException; @@ -28809,6 +28811,7 @@ package android.net { method @Nullable public String getPrivateDnsServerName(); method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(); method public boolean isPrivateDnsActive(); + method public boolean isWakeOnLanSupported(); method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>); method public void setDomains(@Nullable String); method public void setHttpProxy(@Nullable android.net.ProxyInfo); @@ -47912,6 +47915,7 @@ package android.util { ctor public ArraySet(int); ctor public ArraySet(android.util.ArraySet<E>); ctor public ArraySet(java.util.Collection<? extends E>); + ctor public ArraySet(@Nullable E[]); method public boolean add(E); method public void addAll(android.util.ArraySet<? extends E>); method public boolean addAll(java.util.Collection<? extends E>); diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 1d0d2fb70a9d..b5c8e35accad 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -37,6 +37,7 @@ using std::map; using std::string; using std::unordered_map; using std::vector; +using std::shared_ptr; namespace android { namespace os { @@ -67,8 +68,13 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric, const int conditionIndex, const sp<ConditionWizard>& wizard, - const int64_t timeBaseNs, const int64_t startTimeNs) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard) { + const int64_t timeBaseNs, const int64_t startTimeNs, + const unordered_map<int, shared_ptr<Activation>>& + eventActivationMap, + const unordered_map<int, vector<shared_ptr<Activation>>>& + eventDeactivationMap) + : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap, + eventDeactivationMap) { if (metric.has_bucket()) { mBucketSizeNs = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000; diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index b4a910c6f410..61913c71cdcc 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -42,7 +42,11 @@ class CountMetricProducer : public MetricProducer { public: CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex, const sp<ConditionWizard>& wizard, - const int64_t timeBaseNs, const int64_t startTimeNs); + const int64_t timeBaseNs, const int64_t startTimeNs, + const std::unordered_map<int, std::shared_ptr<Activation>>& + eventActivationMap = {}, + const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& + eventDeactivationMap = {}); virtual ~CountMetricProducer(); diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index d7b46d1858cc..31b90f3b13b1 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -36,6 +36,7 @@ using android::util::ProtoOutputStream; using std::string; using std::unordered_map; using std::vector; +using std::shared_ptr; namespace android { namespace os { @@ -62,14 +63,15 @@ const int FIELD_ID_BUCKET_NUM = 4; const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5; const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; -DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const DurationMetric& metric, - const int conditionIndex, const size_t startIndex, - const size_t stopIndex, const size_t stopAllIndex, - const bool nesting, - const sp<ConditionWizard>& wizard, - const FieldMatcher& internalDimensions, - const int64_t timeBaseNs, const int64_t startTimeNs) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard), +DurationMetricProducer::DurationMetricProducer( + const ConfigKey& key, const DurationMetric& metric, const int conditionIndex, + const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex, + const bool nesting, const sp<ConditionWizard>& wizard, + const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, + const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, + const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) + : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap, + eventDeactivationMap), mAggregationType(metric.aggregation_type()), mStartIndex(startIndex), mStopIndex(stopIndex), diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 56c9fd68eac5..0592b1808f52 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -42,7 +42,12 @@ public: const int conditionIndex, const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex, const bool nesting, const sp<ConditionWizard>& wizard, - const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs); + const FieldMatcher& internalDimensions, const int64_t timeBaseNs, + const int64_t startTimeNs, + const unordered_map<int, shared_ptr<Activation>>& + eventActivationMap = {}, + const unordered_map<int, vector<shared_ptr<Activation>>>& + eventDeactivationMap = {}); virtual ~DurationMetricProducer(); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 96133bd0a38d..a60a9161bdbb 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -36,6 +36,7 @@ using std::map; using std::string; using std::unordered_map; using std::vector; +using std::shared_ptr; namespace android { namespace os { @@ -54,8 +55,13 @@ const int FIELD_ID_ATOMS = 2; EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric, const int conditionIndex, const sp<ConditionWizard>& wizard, - const int64_t startTimeNs) - : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) { + const int64_t startTimeNs, + const unordered_map<int, shared_ptr<Activation>>& + eventActivationMap, + const unordered_map<int, vector<shared_ptr<Activation>>>& + eventDeactivationMap) + : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard, eventActivationMap, + eventDeactivationMap) { if (metric.links().size() > 0) { for (const auto& link : metric.links()) { Metric2Condition mc; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 74e6bc845c04..aab53c8b6816 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -35,7 +35,11 @@ class EventMetricProducer : public MetricProducer { public: EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex, const sp<ConditionWizard>& wizard, - const int64_t startTimeNs); + const int64_t startTimeNs, + const std::unordered_map<int, std::shared_ptr<Activation>>& + eventActivationMap = {}, + const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& + eventDeactivationMap = {}); virtual ~EventMetricProducer(); diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index efd05dcb04f1..e409b6fbb9e9 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -72,8 +72,11 @@ GaugeMetricProducer::GaugeMetricProducer( const sp<ConditionWizard>& wizard, const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId, const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs, - const sp<StatsPullerManager>& pullerManager) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard), + const sp<StatsPullerManager>& pullerManager, + const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, + const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) + : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap, + eventDeactivationMap), mWhatMatcherIndex(whatMatcherIndex), mEventMatcherWizard(matcherWizard), mPullerManager(pullerManager), @@ -133,8 +136,11 @@ GaugeMetricProducer::GaugeMetricProducer( mBucketSizeNs); } - // Adjust start for partial bucket + // Adjust start for partial first bucket and then pull if needed mCurrentBucketStartTimeNs = startTimeNs; + if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { + pullAndMatchEventsLocked(mCurrentBucketStartTimeNs); + } VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs, @@ -295,11 +301,6 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, } } -void GaugeMetricProducer::prepareFirstBucketLocked() { - if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - pullAndMatchEventsLocked(mCurrentBucketStartTimeNs); - } -} void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { bool triggerPuller = false; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index a612adf8a38b..dfe1d56c61c7 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -58,11 +58,14 @@ class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDa public: GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex, const sp<ConditionWizard>& conditionWizard, - const int whatMatcherIndex, - const sp<EventMatcherWizard>& matcherWizard, + const int whatMatcherIndex,const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId, const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs, - const sp<StatsPullerManager>& pullerManager); + const sp<StatsPullerManager>& pullerManager, + const std::unordered_map<int, std::shared_ptr<Activation>>& + eventActivationMap = {}, + const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& + eventDeactivationMap = {}); virtual ~GaugeMetricProducer(); @@ -125,8 +128,6 @@ private: void flushCurrentBucketLocked(const int64_t& eventTimeNs, const int64_t& nextBucketStartTimeNs) override; - void prepareFirstBucketLocked() override; - void pullAndMatchEventsLocked(const int64_t timestampNs); const int mWhatMatcherIndex; diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 1ab4fdf6e90c..3426a195a748 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -40,6 +40,29 @@ const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1; const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2; const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3; +MetricProducer::MetricProducer( + const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs, + const int conditionIndex, const sp<ConditionWizard>& wizard, + const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap, + const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& + eventDeactivationMap) + : mMetricId(metricId), + mConfigKey(key), + mTimeBaseNs(timeBaseNs), + mCurrentBucketStartTimeNs(timeBaseNs), + mCurrentBucketNum(0), + mCondition(initialCondition(conditionIndex)), + mConditionTrackerIndex(conditionIndex), + mConditionSliced(false), + mWizard(wizard), + mContainANYPositionInDimensionsInWhat(false), + mSliceByPositionALL(false), + mHasLinksToAllConditionDimensionsInTracker(false), + mEventActivationMap(eventActivationMap), + mEventDeactivationMap(eventDeactivationMap), + mIsActive(mEventActivationMap.empty()) { + } + void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { if (!mIsActive) { return; @@ -97,24 +120,6 @@ void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) { } } -void MetricProducer::addActivation(int activationTrackerIndex, const ActivationType& activationType, - int64_t ttl_seconds, int deactivationTrackerIndex) { - std::lock_guard<std::mutex> lock(mMutex); - // When a metric producer does not depend on any activation, its mIsActive is true. - // Therefore, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not - // change. - if (mEventActivationMap.empty()) { - mIsActive = false; - } - std::shared_ptr<Activation> activation = - std::make_shared<Activation>(activationType, ttl_seconds * NS_PER_SEC); - mEventActivationMap.emplace(activationTrackerIndex, activation); - if (-1 != deactivationTrackerIndex) { - auto& deactivationList = mEventDeactivationMap[deactivationTrackerIndex]; - deactivationList.push_back(activation); - } -} - void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) { auto it = mEventActivationMap.find(activationTrackerIndex); if (it == mEventActivationMap.end()) { diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index fdbdc83fb66e..1e1eb69aa8f0 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -69,6 +69,19 @@ enum DumpLatency { NO_TIME_CONSTRAINTS = 2 }; +struct Activation { + Activation(const ActivationType& activationType, const int64_t ttlNs) + : ttl_ns(ttlNs), + start_ns(0), + state(ActivationState::kNotActive), + activationType(activationType) {} + + const int64_t ttl_ns; + int64_t start_ns; + ActivationState state; + const ActivationType activationType; +}; + // A MetricProducer is responsible for compute one single metrics, creating stats log report, and // writing the report to dropbox. MetricProducers should respond to package changes as required in // PackageInfoListener, but if none of the metrics are slicing by package name, then the update can @@ -76,21 +89,10 @@ enum DumpLatency { class MetricProducer : public virtual PackageInfoListener { public: MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs, - const int conditionIndex, const sp<ConditionWizard>& wizard) - : mMetricId(metricId), - mConfigKey(key), - mTimeBaseNs(timeBaseNs), - mCurrentBucketStartTimeNs(timeBaseNs), - mCurrentBucketNum(0), - mCondition(initialCondition(conditionIndex)), - mConditionTrackerIndex(conditionIndex), - mConditionSliced(false), - mWizard(wizard), - mContainANYPositionInDimensionsInWhat(false), - mSliceByPositionALL(false), - mHasLinksToAllConditionDimensionsInTracker(false), - mIsActive(true) { - } + const int conditionIndex, const sp<ConditionWizard>& wizard, + const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap, + const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& + eventDeactivationMap); virtual ~MetricProducer(){}; @@ -188,11 +190,6 @@ public: dropDataLocked(dropTimeNs); } - void prepareFirstBucket() { - std::lock_guard<std::mutex> lock(mMutex); - prepareFirstBucketLocked(); - } - void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) { std::lock_guard<std::mutex> lock(mMutex); loadActiveMetricLocked(activeMetric, currentTimeNs); @@ -215,9 +212,6 @@ public: void flushIfExpire(int64_t elapsedTimestampNs); - void addActivation(int activationTrackerIndex, const ActivationType& activationType, - int64_t ttl_seconds, int deactivationTrackerIndex = -1); - void writeActiveMetricToProtoOutputStream( int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); @@ -310,7 +304,6 @@ protected: virtual size_t byteSizeLocked() const = 0; virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0; virtual void dropDataLocked(const int64_t dropTimeNs) = 0; - virtual void prepareFirstBucketLocked() {}; void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs); void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs); void cancelEventActivationLocked(int deactivationTrackerIndex); @@ -379,19 +372,6 @@ protected: mutable std::mutex mMutex; - struct Activation { - Activation(const ActivationType& activationType, const int64_t ttlNs) - : ttl_ns(ttlNs), - start_ns(0), - state(ActivationState::kNotActive), - activationType(activationType) {} - - const int64_t ttl_ns; - int64_t start_ns; - ActivationState state; - const ActivationType activationType; - }; - // When the metric producer has multiple activations, these activations are ORed to determine // whether the metric producer is ready to generate metrics. std::unordered_map<int, std::shared_ptr<Activation>> mEventActivationMap; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index bc1602497c39..7fe5a834f412 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -81,8 +81,11 @@ ValueMetricProducer::ValueMetricProducer( const ConfigKey& key, const ValueMetric& metric, const int conditionIndex, const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs, - const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard), + const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager, + const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, + const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) + : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard, + eventActivationMap, eventDeactivationMap), mWhatMatcherIndex(whatMatcherIndex), mEventMatcherWizard(matcherWizard), mPullerManager(pullerManager), @@ -108,7 +111,7 @@ ValueMetricProducer::ValueMetricProducer( mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC : StatsdStats::kPullMaxDelayNs), mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()), - // Condition timer will be set in prepareFirstBucketLocked. + // Condition timer will be set later within the constructor after pulling events mConditionTimer(false, timeBaseNs) { int64_t bucketSizeMills = 0; if (metric.has_bucket()) { @@ -154,6 +157,15 @@ ValueMetricProducer::ValueMetricProducer( // Adjust start for partial bucket mCurrentBucketStartTimeNs = startTimeNs; mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs); + + // Kicks off the puller immediately if condition is true and diff based. + if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) { + pullAndMatchEventsLocked(mCurrentBucketStartTimeNs, mCondition); + } + // Now that activations are processed, start the condition timer if needed. + mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue, + mCurrentBucketStartTimeNs); + VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs); } @@ -165,16 +177,6 @@ ValueMetricProducer::~ValueMetricProducer() { } } -void ValueMetricProducer::prepareFirstBucketLocked() { - // Kicks off the puller immediately if condition is true and diff based. - if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) { - pullAndMatchEventsLocked(mCurrentBucketStartTimeNs, mCondition); - } - // Now that activations are processed, start the condition timer if needed. - mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue, - mCurrentBucketStartTimeNs); -} - void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) { VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 739f6ef07cc4..d7cd397da2c0 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -54,10 +54,13 @@ class ValueMetricProducer : public virtual MetricProducer, public virtual PullDa public: ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex, const sp<ConditionWizard>& conditionWizard, - const int whatMatcherIndex, - const sp<EventMatcherWizard>& matcherWizard, + const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs, - const sp<StatsPullerManager>& pullerManager); + const sp<StatsPullerManager>& pullerManager, + const std::unordered_map<int, std::shared_ptr<Activation>>& + eventActivationMap = {}, + const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& + eventDeactivationMap = {}); virtual ~ValueMetricProducer(); @@ -116,8 +119,6 @@ private: void flushCurrentBucketLocked(const int64_t& eventTimeNs, const int64_t& nextBucketStartTimeNs) override; - void prepareFirstBucketLocked() override; - void dropDataLocked(const int64_t dropTimeNs) override; // Calculate previous bucket end time based on current time. diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 40484f4fb86b..0fee71e1fe2c 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "metrics_manager_util.h" +#include "MetricProducer.h" #include "../condition/CombinationConditionTracker.h" #include "../condition/SimpleConditionTracker.h" @@ -137,6 +138,62 @@ bool handleMetricWithConditions( return true; } +// Validates a metricActivation and populates state. +// EventActivationMap and EventDeactivationMap are supplied to a MetricProducer +// to provide the producer with state about its activators and deactivators. +// Returns false if there are errors. +bool handleMetricActivation( + const StatsdConfig& config, + const int64_t metricId, + const int metricIndex, + const unordered_map<int64_t, int>& metricToActivationMap, + const unordered_map<int64_t, int>& logTrackerMap, + unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation, + unordered_map<int, shared_ptr<Activation>>& eventActivationMap, + unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) { + // Check if metric has an associated activation + auto itr = metricToActivationMap.find(metricId); + if (itr == metricToActivationMap.end()) return true; + + int activationIndex = itr->second; + const MetricActivation& metricActivation = config.metric_activation(activationIndex); + + for (int i = 0; i < metricActivation.event_activation_size(); i++) { + const EventActivation& activation = metricActivation.event_activation(i); + + auto itr = logTrackerMap.find(activation.atom_matcher_id()); + if (itr == logTrackerMap.end()) { + ALOGE("Atom matcher not found for event activation."); + return false; + } + + ActivationType activationType = (activation.has_activation_type()) ? + activation.activation_type() : metricActivation.activation_type(); + std::shared_ptr<Activation> activationWrapper = std::make_shared<Activation>( + activationType, activation.ttl_seconds() * NS_PER_SEC); + + int atomMatcherIndex = itr->second; + activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex); + eventActivationMap.emplace(atomMatcherIndex, activationWrapper); + + if (activation.has_deactivation_atom_matcher_id()) { + itr = logTrackerMap.find(activation.deactivation_atom_matcher_id()); + if (itr == logTrackerMap.end()) { + ALOGE("Atom matcher not found for event deactivation."); + return false; + } + int deactivationAtomMatcherIndex = itr->second; + deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex); + eventDeactivationMap[deactivationAtomMatcherIndex].push_back(activationWrapper); + } + } + + metricsWithActivation.push_back(metricIndex); + return true; +} + bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap, unordered_map<int64_t, int>& logTrackerMap, vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) { @@ -293,16 +350,33 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t const vector<sp<LogMatchingTracker>>& allAtomMatchers, vector<sp<ConditionTracker>>& allConditionTrackers, vector<sp<MetricProducer>>& allMetricProducers, - unordered_map<int, std::vector<int>>& conditionToMetricMap, - unordered_map<int, std::vector<int>>& trackerToMetricMap, - unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds) { + unordered_map<int, vector<int>>& conditionToMetricMap, + unordered_map<int, vector<int>>& trackerToMetricMap, + unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds, + unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation) { sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers); sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchers); const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() + - config.event_metric_size() + config.value_metric_size(); + config.event_metric_size() + config.gauge_metric_size() + + config.value_metric_size(); allMetricProducers.reserve(allMetricsCount); StatsPullerManager statsPullerManager; + // Construct map from metric id to metric activation index. The map will be used to determine + // the metric activation corresponding to a metric. + unordered_map<int64_t, int> metricToActivationMap; + for (int i = 0; i < config.metric_activation_size(); i++) { + const MetricActivation& metricActivation = config.metric_activation(i); + int64_t metricId = metricActivation.metric_id(); + if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) { + ALOGE("Metric %lld has multiple MetricActivations", (long long) metricId); + return false; + } + metricToActivationMap.insert({metricId, i}); + } + // Build MetricProducers for each metric defined in config. // build CountMetricProducer for (int i = 0; i < config.count_metric_size(); i++) { @@ -337,8 +411,17 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } - sp<MetricProducer> countProducer = - new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs); + unordered_map<int, shared_ptr<Activation>> eventActivationMap; + unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; + bool success = handleMetricActivation(config, metric.id(), metricIndex, + metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, + eventDeactivationMap); + if (!success) return false; + + sp<MetricProducer> countProducer = new CountMetricProducer( + key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs, + eventActivationMap, eventDeactivationMap); allMetricProducers.push_back(countProducer); } @@ -406,9 +489,18 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } + unordered_map<int, shared_ptr<Activation>> eventActivationMap; + unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; + bool success = handleMetricActivation(config, metric.id(), metricIndex, + metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, + eventDeactivationMap); + if (!success) return false; + sp<MetricProducer> durationMetric = new DurationMetricProducer( key, metric, conditionIndex, trackerIndices[0], trackerIndices[1], - trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs, currentTimeNs); + trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs, + currentTimeNs, eventActivationMap, eventDeactivationMap); allMetricProducers.push_back(durationMetric); } @@ -443,8 +535,17 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } - sp<MetricProducer> eventMetric = - new EventMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs); + unordered_map<int, shared_ptr<Activation>> eventActivationMap; + unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; + bool success = handleMetricActivation(config, metric.id(), metricIndex, + metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, + eventDeactivationMap); + if (!success) return false; + + sp<MetricProducer> eventMetric = new EventMetricProducer( + key, metric, conditionIndex, wizard, timeBaseTimeNs, eventActivationMap, + eventDeactivationMap); allMetricProducers.push_back(eventMetric); } @@ -500,9 +601,18 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } + unordered_map<int, shared_ptr<Activation>> eventActivationMap; + unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; + bool success = handleMetricActivation(config, metric.id(), metricIndex, + metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, + eventDeactivationMap); + if (!success) return false; + sp<MetricProducer> valueProducer = new ValueMetricProducer( key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId, - timeBaseTimeNs, currentTimeNs, pullerManager); + timeBaseTimeNs, currentTimeNs, pullerManager, eventActivationMap, + eventDeactivationMap); allMetricProducers.push_back(valueProducer); } @@ -586,10 +696,19 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } + unordered_map<int, shared_ptr<Activation>> eventActivationMap; + unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; + bool success = handleMetricActivation(config, metric.id(), metricIndex, + metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, + eventDeactivationMap); + if (!success) return false; + sp<MetricProducer> gaugeProducer = new GaugeMetricProducer( key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId, - timeBaseTimeNs, currentTimeNs, pullerManager); + timeBaseTimeNs, currentTimeNs, pullerManager, + eventActivationMap, eventDeactivationMap); allMetricProducers.push_back(gaugeProducer); } for (int i = 0; i < config.no_report_metric_size(); ++i) { @@ -707,73 +826,6 @@ bool initAlarms(const StatsdConfig& config, const ConfigKey& key, return true; } -bool initMetricActivations(const ConfigKey& key, const StatsdConfig& config, - const int64_t currentTimeNs, - const unordered_map<int64_t, int> &logEventTrackerMap, - const unordered_map<int64_t, int> &metricProducerMap, - vector<sp<MetricProducer>>& allMetricProducers, - unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, - unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, - vector<int>& metricsWithActivation) { - for (int i = 0; i < config.metric_activation_size(); ++i) { - const MetricActivation& metric_activation = config.metric_activation(i); - auto itr = metricProducerMap.find(metric_activation.metric_id()); - if (itr == metricProducerMap.end()) { - ALOGE("Metric id not found in metric activation: %lld", - (long long)metric_activation.metric_id()); - return false; - } - const int metricTrackerIndex = itr->second; - if (metricTrackerIndex < 0 || metricTrackerIndex >= (int)allMetricProducers.size()) { - ALOGE("Invalid metric tracker index."); - return false; - } - const sp<MetricProducer>& metric = allMetricProducers[metricTrackerIndex]; - metricsWithActivation.push_back(metricTrackerIndex); - for (int j = 0; j < metric_activation.event_activation_size(); ++j) { - const EventActivation& activation = metric_activation.event_activation(j); - auto logTrackerIt = logEventTrackerMap.find(activation.atom_matcher_id()); - if (logTrackerIt == logEventTrackerMap.end()) { - ALOGE("Atom matcher not found for event activation."); - return false; - } - const int atomMatcherIndex = logTrackerIt->second; - activationAtomTrackerToMetricMap[atomMatcherIndex].push_back( - metricTrackerIndex); - - ActivationType activationType; - if (activation.has_activation_type()) { - activationType = activation.activation_type(); - } else { - activationType = metric_activation.activation_type(); - } - - if (activation.has_deactivation_atom_matcher_id()) { - auto deactivationAtomMatcherIt = - logEventTrackerMap.find(activation.deactivation_atom_matcher_id()); - if (deactivationAtomMatcherIt == logEventTrackerMap.end()) { - ALOGE("Atom matcher not found for event deactivation."); - return false; - } - const int deactivationMatcherIndex = deactivationAtomMatcherIt->second; - deactivationAtomTrackerToMetricMap[deactivationMatcherIndex] - .push_back(metricTrackerIndex); - metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds(), - deactivationMatcherIndex); - } else { - metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds()); - } - } - } - return true; -} - -void prepareFirstBucket(const vector<sp<MetricProducer>>& allMetricProducers) { - for (const auto& metric: allMetricProducers) { - metric->prepareFirstBucket(); - } -} - bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap, const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor, @@ -810,7 +862,8 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap, pullerManager, logTrackerMap, conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers, conditionToMetricMap, trackerToMetricMap, metricProducerMap, - noReportMetricIds)) { + noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation)) { ALOGE("initMetricProducers failed"); return false; } @@ -824,14 +877,6 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& ALOGE("initAlarms failed"); return false; } - if (!initMetricActivations(key, config, currentTimeNs, logTrackerMap, metricProducerMap, - allMetricProducers, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation)) { - ALOGE("initMetricActivations failed"); - return false; - } - - prepareFirstBucket(allMetricProducers); return true; } diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index 3704969039c4..3802948f64da 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -91,7 +91,10 @@ bool initMetrics( std::vector<sp<MetricProducer>>& allMetricProducers, std::unordered_map<int, std::vector<int>>& conditionToMetricMap, std::unordered_map<int, std::vector<int>>& trackerToMetricMap, - std::set<int64_t>& noReportMetricIds); + std::set<int64_t>& noReportMetricIds, + std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::vector<int>& metricsWithActivation); // Initialize MetricsManager from StatsdConfig. // Parameters are the members of MetricsManager. See MetricsManager for declaration. diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 47c21aa8f1df..b027e8e687db 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -79,8 +79,6 @@ TEST(GaugeMetricProducerTest, TestFirstBucket) { logEventMatcherIndex, eventMatcherWizard, -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager); - gaugeProducer.prepareFirstBucket(); - EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum); @@ -126,8 +124,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); - vector<shared_ptr<LogEvent>> allData; allData.clear(); shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); @@ -211,7 +207,6 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { logEventMatcherIndex, eventMatcherWizard, -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); @@ -303,7 +298,6 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); @@ -370,7 +364,6 @@ TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); @@ -431,7 +424,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); @@ -521,7 +513,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8); @@ -572,7 +563,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); Alert alert; alert.set_id(101); @@ -681,7 +671,6 @@ TEST(GaugeMetricProducerTest, TestPullOnTrigger) { logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -766,7 +755,6 @@ TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 2262c76e64f9..4b9d0c0ca718 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -105,7 +105,6 @@ class ValueMetricProducerTestHelper { kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); return valueProducer; } @@ -125,7 +124,6 @@ class ValueMetricProducerTestHelper { new ValueMetricProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); valueProducer->mCondition = ConditionState::kFalse; return valueProducer; } @@ -169,7 +167,6 @@ TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) { ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex, eventMatcherWizard, -1, startTimeBase, 22, pullerManager); - valueProducer.prepareFirstBucket(); EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10)); EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10)); @@ -199,7 +196,6 @@ TEST(ValueMetricProducerTest, TestFirstBucket) { ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex, eventMatcherWizard, -1, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager); - valueProducer.prepareFirstBucket(); EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(10, valueProducer.mCurrentBucketNum); @@ -381,7 +377,6 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -670,7 +665,6 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); event1->write(1); @@ -728,7 +722,6 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -779,7 +772,6 @@ TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -854,7 +846,6 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); event1->write(1); @@ -897,7 +888,6 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); valueProducer.mCondition = ConditionState::kFalse; shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); @@ -972,7 +962,6 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex, eventMatcherWizard, -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor); @@ -1269,7 +1258,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); event1->write(1); @@ -1314,7 +1302,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); event1->write(1); @@ -1361,7 +1348,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); event1->write(1); @@ -1412,7 +1398,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); event1->write(1); @@ -1458,7 +1443,6 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); event1->write(1); @@ -1532,7 +1516,6 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); event1->write(1); @@ -2081,7 +2064,6 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucket2StartTimeNs, bucket2StartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); valueProducer.mCondition = ConditionState::kFalse; // Event should be skipped since it is from previous bucket. @@ -2862,7 +2844,6 @@ TEST(ValueMetricProducerTest, TestPullNeededFastDump) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); ProtoOutputStream output; std::set<string> strSet; @@ -2905,7 +2886,6 @@ TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -2969,7 +2949,6 @@ TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); ProtoOutputStream output; std::set<string> strSet; diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index a2013075b379..86bf20a57eec 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -3169,6 +3169,15 @@ public class ApplicationPackageManager extends PackageManager { } @Override + public String getSetupWizardPackageName() { + try { + return mPM.getSetupWizardPackageName(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override public String getIncidentReportApproverPackageName() { try { return mPM.getIncidentReportApproverPackageName(); diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index b873be3e7042..713126ee9341 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -24,6 +24,10 @@ import java.util.List; /** * Device policy manager local system service interface. * + * Maintenance note: if you need to expose information from DPMS to lower level services such as + * PM/UM/AM/etc, then exposing it from DevicePolicyManagerInternal is not safe because it may cause + * lock order inversion. Consider using {@link DevicePolicyCache} instead. + * * @hide Only for use within the system server. */ public abstract class DevicePolicyManagerInternal { @@ -81,6 +85,16 @@ public abstract class DevicePolicyManagerInternal { public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy); /** + * Checks if an app with given uid is the active supervision admin. + * + * <p>This takes the DPMS lock. DO NOT call from PM/UM/AM with their lock held. + * + * @param uid App uid. + * @return true if the uid is the active supervision app. + */ + public abstract boolean isActiveSupervisionApp(int uid); + + /** * Creates an intent to show the admin support dialog to say that an action is disallowed by * the device/profile owner. * diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 656f474f72ce..1f13a1e13d13 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -801,8 +801,8 @@ public final class UsageStatsManager { * {@link #EXTRA_TIME_USED}. Cannot be {@code null} unless the observer is * being registered with a {@code timeUsed} equal to or greater than * {@code timeLimit}. - * @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE - * permissions. + * @throws SecurityException if the caller is neither the active supervision app nor does it + * have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions. * @hide */ @SystemApi @@ -827,8 +827,8 @@ public final class UsageStatsManager { * an observer that was already unregistered or never registered will have no effect. * * @param observerId The id of the observer that was previously registered. - * @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE - * permissions. + * @throws SecurityException if the caller is neither the active supervision app nor does it + * have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions. * @hide */ @SystemApi diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 4d7c43ace923..19d8edfa3884 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -686,6 +686,8 @@ interface IPackageManager { String getSystemCaptionsServicePackageName(); + String getSetupWizardPackageName(); + String getIncidentReportApproverPackageName(); boolean isPackageStateProtected(String packageName, int userId); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index fd14d235a044..9513ce802813 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -7427,6 +7427,17 @@ public abstract class PackageManager { } /** + * @return the system defined setup wizard package name, or null if there's none. + * + * @hide + */ + @Nullable + public String getSetupWizardPackageName() { + throw new UnsupportedOperationException( + "getSetupWizardPackageName not implemented in subclass"); + } + + /** * @return the incident report approver app package name, or null if it's not defined * by the OEM. * diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index d3f48acdd40e..3ec0aeac472b 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -68,6 +68,7 @@ public final class LinkProperties implements Parcelable { // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max" private String mTcpBufferSizes; private IpPrefix mNat64Prefix; + private boolean mWakeOnLanSupported; private static final int MIN_MTU = 68; private static final int MIN_MTU_V6 = 1280; @@ -193,6 +194,7 @@ public final class LinkProperties implements Parcelable { setMtu(source.mMtu); mTcpBufferSizes = source.mTcpBufferSizes; mNat64Prefix = source.mNat64Prefix; + mWakeOnLanSupported = source.mWakeOnLanSupported; } } @@ -852,6 +854,7 @@ public final class LinkProperties implements Parcelable { mMtu = 0; mTcpBufferSizes = null; mNat64Prefix = null; + mWakeOnLanSupported = false; } /** @@ -913,6 +916,10 @@ public final class LinkProperties implements Parcelable { resultJoiner.add("MTU:"); resultJoiner.add(Integer.toString(mMtu)); + if (mWakeOnLanSupported) { + resultJoiner.add("WakeOnLanSupported: true"); + } + if (mTcpBufferSizes != null) { resultJoiner.add("TcpBufferSizes:"); resultJoiner.add(mTcpBufferSizes); @@ -1425,6 +1432,37 @@ public final class LinkProperties implements Parcelable { } /** + * Compares this {@code LinkProperties} WakeOnLan supported against the target. + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + * @hide + */ + public boolean isIdenticalWakeOnLan(LinkProperties target) { + return isWakeOnLanSupported() == target.isWakeOnLanSupported(); + } + + /** + * Set whether the network interface supports WakeOnLAN + * + * @param supported WakeOnLAN supported value + * + * @hide + */ + public void setWakeOnLanSupported(boolean supported) { + mWakeOnLanSupported = supported; + } + + /** + * Returns whether the network interface supports WakeOnLAN + * + * @return {@code true} if interface supports WakeOnLAN, {@code false} otherwise. + */ + public boolean isWakeOnLanSupported() { + return mWakeOnLanSupported; + } + + /** * Compares this {@code LinkProperties} instance against the target * LinkProperties in {@code obj}. Two LinkPropertieses are equal if * all their fields are equal in values. @@ -1461,7 +1499,8 @@ public final class LinkProperties implements Parcelable { && isIdenticalStackedLinks(target) && isIdenticalMtu(target) && isIdenticalTcpBufferSizes(target) - && isIdenticalNat64Prefix(target); + && isIdenticalNat64Prefix(target) + && isIdenticalWakeOnLan(target); } /** @@ -1577,7 +1616,8 @@ public final class LinkProperties implements Parcelable { + (mUsePrivateDns ? 57 : 0) + mPcscfs.size() * 67 + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode()) - + Objects.hash(mNat64Prefix); + + Objects.hash(mNat64Prefix) + + (mWakeOnLanSupported ? 71 : 0); } /** @@ -1622,6 +1662,8 @@ public final class LinkProperties implements Parcelable { ArrayList<LinkProperties> stackedLinks = new ArrayList<>(mStackedLinks.values()); dest.writeList(stackedLinks); + + dest.writeBoolean(mWakeOnLanSupported); } /** @@ -1677,6 +1719,7 @@ public final class LinkProperties implements Parcelable { for (LinkProperties stackedLink: stackedLinks) { netProp.addStackedLink(stackedLink); } + netProp.setWakeOnLanSupported(in.readBoolean()); return netProp; } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index b3125d89a1a7..6a709b56f35a 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -243,7 +243,8 @@ public class Build { public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", ""); /** - * The user-visible security patch level. + * The user-visible security patch level. This value represents the date when the device + * most recently applied a security patch. */ public static final String SECURITY_PATCH = SystemProperties.get( "ro.build.version.security_patch", ""); diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java index cfb582ef442e..5e8929c6c999 100644 --- a/core/java/android/os/HwParcel.java +++ b/core/java/android/os/HwParcel.java @@ -23,6 +23,8 @@ import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import dalvik.annotation.optimization.FastNative; + import libcore.util.NativeAllocationRegistry; import java.lang.annotation.Retention; @@ -72,46 +74,54 @@ public class HwParcel { /** * Writes an interface token into the parcel used to verify that - * a transaction has made it to the write type of interface. + * a transaction has made it to the right type of interface. * * @param interfaceName fully qualified name of interface message * is being sent to. */ + @FastNative public native final void writeInterfaceToken(String interfaceName); /** * Writes a boolean value to the end of the parcel. * @param val to write */ + @FastNative public native final void writeBool(boolean val); /** * Writes a byte value to the end of the parcel. * @param val to write */ + @FastNative public native final void writeInt8(byte val); /** * Writes a short value to the end of the parcel. * @param val to write */ + @FastNative public native final void writeInt16(short val); /** * Writes a int value to the end of the parcel. * @param val to write */ + @FastNative public native final void writeInt32(int val); /** * Writes a long value to the end of the parcel. * @param val to write */ + @FastNative public native final void writeInt64(long val); /** * Writes a float value to the end of the parcel. * @param val to write */ + @FastNative public native final void writeFloat(float val); /** * Writes a double value to the end of the parcel. * @param val to write */ + @FastNative public native final void writeDouble(double val); /** * Writes a String value to the end of the parcel. @@ -120,6 +130,7 @@ public class HwParcel { * * @param val to write */ + @FastNative public native final void writeString(String val); /** * Writes a native handle (without duplicating the underlying @@ -127,42 +138,50 @@ public class HwParcel { * * @param val to write */ + @FastNative public native final void writeNativeHandle(@Nullable NativeHandle val); /** * Writes an array of boolean values to the end of the parcel. * @param val to write */ + @FastNative private native final void writeBoolVector(boolean[] val); /** * Writes an array of byte values to the end of the parcel. * @param val to write */ + @FastNative private native final void writeInt8Vector(byte[] val); /** * Writes an array of short values to the end of the parcel. * @param val to write */ + @FastNative private native final void writeInt16Vector(short[] val); /** * Writes an array of int values to the end of the parcel. * @param val to write */ + @FastNative private native final void writeInt32Vector(int[] val); /** * Writes an array of long values to the end of the parcel. * @param val to write */ + @FastNative private native final void writeInt64Vector(long[] val); /** * Writes an array of float values to the end of the parcel. * @param val to write */ + @FastNative private native final void writeFloatVector(float[] val); /** * Writes an array of double values to the end of the parcel. * @param val to write */ + @FastNative private native final void writeDoubleVector(double[] val); /** * Writes an array of String values to the end of the parcel. @@ -171,6 +190,7 @@ public class HwParcel { * * @param val to write */ + @FastNative private native final void writeStringVector(String[] val); /** * Writes an array of native handles to the end of the parcel. @@ -179,6 +199,7 @@ public class HwParcel { * * @param val array of {@link NativeHandle} objects to write */ + @FastNative private native final void writeNativeHandleVector(NativeHandle[] val); /** @@ -299,6 +320,7 @@ public class HwParcel { * Write a hwbinder object to the end of the parcel. * @param binder value to write */ + @FastNative public native final void writeStrongBinder(IHwBinder binder); /** @@ -314,48 +336,56 @@ public class HwParcel { * @return value parsed from the parcel * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative public native final boolean readBool(); /** * Reads a byte value from the current location in the parcel. * @return value parsed from the parcel * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative public native final byte readInt8(); /** * Reads a short value from the current location in the parcel. * @return value parsed from the parcel * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative public native final short readInt16(); /** * Reads a int value from the current location in the parcel. * @return value parsed from the parcel * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative public native final int readInt32(); /** * Reads a long value from the current location in the parcel. * @return value parsed from the parcel * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative public native final long readInt64(); /** * Reads a float value from the current location in the parcel. * @return value parsed from the parcel * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative public native final float readFloat(); /** * Reads a double value from the current location in the parcel. * @return value parsed from the parcel * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative public native final double readDouble(); /** * Reads a String value from the current location in the parcel. * @return value parsed from the parcel * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative public native final String readString(); /** * Reads a native handle (without duplicating the underlying file @@ -366,6 +396,7 @@ public class HwParcel { * @return a {@link NativeHandle} instance parsed from the parcel * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative public native final @Nullable NativeHandle readNativeHandle(); /** * Reads an embedded native handle (without duplicating the underlying @@ -379,6 +410,7 @@ public class HwParcel { * @return a {@link NativeHandle} instance parsed from the parcel * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative public native final @Nullable NativeHandle readEmbeddedNativeHandle( long parentHandle, long offset); @@ -387,54 +419,63 @@ public class HwParcel { * @return array of parsed values * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative private native final boolean[] readBoolVectorAsArray(); /** * Reads an array of byte values from the parcel. * @return array of parsed values * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative private native final byte[] readInt8VectorAsArray(); /** * Reads an array of short values from the parcel. * @return array of parsed values * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative private native final short[] readInt16VectorAsArray(); /** * Reads an array of int values from the parcel. * @return array of parsed values * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative private native final int[] readInt32VectorAsArray(); /** * Reads an array of long values from the parcel. * @return array of parsed values * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative private native final long[] readInt64VectorAsArray(); /** * Reads an array of float values from the parcel. * @return array of parsed values * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative private native final float[] readFloatVectorAsArray(); /** * Reads an array of double values from the parcel. * @return array of parsed values * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative private native final double[] readDoubleVectorAsArray(); /** * Reads an array of String values from the parcel. * @return array of parsed values * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative private native final String[] readStringVectorAsArray(); /** * Reads an array of native handles from the parcel. * @return array of {@link NativeHandle} objects * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative private native final NativeHandle[] readNativeHandleAsArray(); /** @@ -537,6 +578,7 @@ public class HwParcel { * @return binder object read from parcel or null if no binder can be read * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative public native final IHwBinder readStrongBinder(); /** @@ -544,6 +586,7 @@ public class HwParcel { * @return blob of size expectedSize * @throws IllegalArgumentException if the parcel has no more data */ + @FastNative public native final HwBlob readBuffer(long expectedSize); /** @@ -559,6 +602,7 @@ public class HwParcel { * @throws NullPointerException if the transaction specified the blob to be null * but nullable is false */ + @FastNative public native final HwBlob readEmbeddedBuffer( long expectedSize, long parentHandle, long offset, boolean nullable); @@ -567,26 +611,31 @@ public class HwParcel { * Write a buffer into the transaction. * @param blob blob to write into the parcel. */ + @FastNative public native final void writeBuffer(HwBlob blob); /** * Write a status value into the blob. * @param status value to write */ + @FastNative public native final void writeStatus(int status); /** * @throws IllegalArgumentException if a success vaue cannot be read * @throws RemoteException if success value indicates a transaction error */ + @FastNative public native final void verifySuccess(); /** * Should be called to reduce memory pressure when this object no longer needs * to be written to. */ + @FastNative public native final void releaseTemporaryStorage(); /** * Should be called when object is no longer needed to reduce possible memory * pressure if the Java GC does not get to this object in time. */ + @FastNative public native final void release(); /** @@ -597,6 +646,7 @@ public class HwParcel { // Returns address of the "freeFunction". private static native final long native_init(); + @FastNative private native final void native_setup(boolean allocate); static { diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java index 44c5af20d891..4dda709f285d 100644 --- a/core/java/android/util/ArraySet.java +++ b/core/java/android/util/ArraySet.java @@ -16,6 +16,7 @@ package android.util; +import android.annotation.Nullable; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; @@ -329,6 +330,18 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { } /** + * Create a new ArraySet with items from the given array + */ + public ArraySet(@Nullable E[] array) { + this(); + if (array != null) { + for (E value : array) { + add(value); + } + } + } + + /** * Make the array map empty. All storage is released. */ @Override diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index b66764e81e8f..1be57dd489cf 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -59,6 +59,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false"); DEFAULT_FLAGS.put("settings_skip_direction_mutable", "true"); DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "false"); + DEFAULT_FLAGS.put("settings_work_profile", "false"); } /** diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 0505fe30c55c..0feab7f61918 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -257,8 +257,6 @@ cc_library_shared { "libnativeloader_lazy", "libmemunreachable", "libhidlbase", - "libhidltransport", - "libhwbinder", "libvintf", "libnativewindow", "libdl", diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 18f8d7b04c3f..b030b33daf5e 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -798,7 +798,7 @@ broadcast module. This is required in order to bind to the cell broadcast service, and ensures that only the system can forward messages to it. - <p>Protection level: signature|privileged + <p>Protection level: signature @hide --> <permission android:name="android.permission.BIND_CELL_BROADCAST_SERVICE" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7eca699d9eca..3fef7a2dffae 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -433,6 +433,14 @@ --> </string-array> + <!-- Configuration of network interfaces that support WakeOnLAN --> + <string-array translatable="false" name="config_wakeonlan_supported_interfaces"> + <!-- + <item>wlan0</item> + <item>eth0</item> + --> + </string-array> + <!-- Package name for the default CellBroadcastService module [DO NOT TRANSLATE] --> <string name="cellbroadcast_default_package" translatable="false">com.android.cellbroadcastreceiver </string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a8b534061aeb..363bc9ddd75c 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -747,6 +747,7 @@ <java-symbol type="string" name="config_default_dns_server" /> <java-symbol type="string" name="config_ethernet_iface_regex" /> <java-symbol type="array" name="config_ethernet_interfaces" /> + <java-symbol type="array" name="config_wakeonlan_supported_interfaces" /> <java-symbol type="string" name="cellbroadcast_default_package" /> <java-symbol type="string" name="config_forceVoiceInteractionServicePackage" /> <java-symbol type="string" name="config_mms_user_agent" /> diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index f3d68755a513..90e29df45e51 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -145,7 +145,7 @@ public class LocationManager { * Key used for an extra holding a boolean enabled/disabled status value when a provider * enabled/disabled event is broadcast using a PendingIntent. * - * @see #requestLocationUpdates(String, long, long, PendingIntent) + * @see #requestLocationUpdates(String, long, float, PendingIntent) */ public static final String KEY_PROVIDER_ENABLED = "providerEnabled"; @@ -153,7 +153,7 @@ public class LocationManager { * Key used for an extra holding a {@link Location} value when a location change is broadcast * using a PendingIntent. * - * @see #requestLocationUpdates(String, long, long, PendingIntent) + * @see #requestLocationUpdates(String, long, float, PendingIntent) */ public static final String KEY_LOCATION_CHANGED = "location"; @@ -1256,44 +1256,46 @@ public class LocationManager { } /** - * Creates a mock location provider and adds it to the set of active providers. + * Creates a test location provider and adds it to the set of active providers. This provider + * will replace any provider with the same name that exists prior to this call. * - * @param name the provider name + * @param provider the provider name * + * @throws IllegalArgumentException if provider is null * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED * allowed} for your app. - * @throws IllegalArgumentException if a provider with the given name already exists */ public void addTestProvider( - @NonNull String name, boolean requiresNetwork, boolean requiresSatellite, + @NonNull String provider, boolean requiresNetwork, boolean requiresSatellite, boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) { + Preconditions.checkArgument(provider != null, "invalid null provider"); + ProviderProperties properties = new ProviderProperties(requiresNetwork, requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed, supportsBearing, powerRequirement, accuracy); - if (name.matches(LocationProvider.BAD_CHARS_REGEX)) { - throw new IllegalArgumentException("provider name contains illegal character: " + name); - } - try { - mService.addTestProvider(name, properties, mContext.getOpPackageName()); + mService.addTestProvider(provider, properties, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Removes the mock location provider with the given name. + * Removes the test location provider with the given name or does nothing if no such test + * location provider exists. * * @param provider the provider name * + * @throws IllegalArgumentException if provider is null * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED * allowed} for your app. - * @throws IllegalArgumentException if no provider with the given name exists */ public void removeTestProvider(@NonNull String provider) { + Preconditions.checkArgument(provider != null, "invalid null provider"); + try { mService.removeTestProvider(provider, mContext.getOpPackageName()); } catch (RemoteException e) { @@ -1302,60 +1304,53 @@ public class LocationManager { } /** - * Sets a mock location for the given provider. - * <p>This location will be used in place of any actual location from the provider. - * The location object must have a minimum number of fields set to be - * considered a valid LocationProvider Location, as per documentation - * on {@link Location} class. + * Sets a new location for the given test provider. This location will be identiable as a mock + * location to all clients via {@link Location#isFromMockProvider()}. + * + * <p>The location object must have a minimum number of fields set to be considered valid, as + * per documentation on {@link Location} class. * * @param provider the provider name - * @param loc the mock location + * @param location the mock location * * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED * allowed} for your app. - * @throws IllegalArgumentException if no provider with the given name exists - * @throws IllegalArgumentException if the location is incomplete + * @throws IllegalArgumentException if the provider is null or not a test provider + * @throws IllegalArgumentException if the location is null or incomplete */ - public void setTestProviderLocation(@NonNull String provider, @NonNull Location loc) { - if (!loc.isComplete()) { + public void setTestProviderLocation(@NonNull String provider, @NonNull Location location) { + Preconditions.checkArgument(provider != null, "invalid null provider"); + Preconditions.checkArgument(location != null, "invalid null location"); + + if (!location.isComplete()) { IllegalArgumentException e = new IllegalArgumentException( - "Incomplete location object, missing timestamp or accuracy? " + loc); + "Incomplete location object, missing timestamp or accuracy? " + location); if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) { - // just log on old platform (for backwards compatibility) Log.w(TAG, e); - loc.makeComplete(); + location.makeComplete(); } else { - // really throw it! throw e; } } try { - mService.setTestProviderLocation(provider, loc, mContext.getOpPackageName()); + mService.setTestProviderLocation(provider, location, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Removes any mock location associated with the given provider. - * - * @param provider the provider name + * Does nothing. * - * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION - * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED - * allowed} for your app. - * @throws IllegalArgumentException if no provider with the given name exists - * - * @deprecated This function has always been a no-op, and may be removed in the future. + * @deprecated This method has always been a no-op, and may be removed in the future. */ @Deprecated public void clearTestProviderLocation(@NonNull String provider) {} /** - * Sets a mock enabled value for the given provider. This value will be used in place - * of any actual value from the provider. + * Sets the given test provider to be enabled or disabled. * * @param provider the provider name * @param enabled the mock enabled value @@ -1363,9 +1358,11 @@ public class LocationManager { * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED * allowed} for your app. - * @throws IllegalArgumentException if no provider with the given name exists + * @throws IllegalArgumentException if provider is null or not a test provider */ public void setTestProviderEnabled(@NonNull String provider, boolean enabled) { + Preconditions.checkArgument(provider != null, "invalid null provider"); + try { mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName()); } catch (RemoteException e) { @@ -1374,14 +1371,8 @@ public class LocationManager { } /** - * Removes any mock enabled value associated with the given provider. - * - * @param provider the provider name - * - * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION - * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED - * allowed} for your app. - * @throws IllegalArgumentException if no provider with the given name exists + * Equivalent to calling {@link #setTestProviderEnabled(String, boolean)} to disable a test + * provider. * * @deprecated Use {@link #setTestProviderEnabled(String, boolean)} instead. */ diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java index b69a9d79e5b8..52a03b61d21c 100644 --- a/location/java/android/location/LocationProvider.java +++ b/location/java/android/location/LocationProvider.java @@ -53,28 +53,10 @@ public class LocationProvider { @Deprecated public static final int AVAILABLE = 2; - /** - * A regular expression matching characters that may not appear - * in the name of a LocationProvider - * @hide - */ - public static final String BAD_CHARS_REGEX = "[^a-zA-Z0-9]"; - private final String mName; private final ProviderProperties mProperties; - /** - * Constructs a LocationProvider with the given name. Provider names must - * consist only of the characters [a-zA-Z0-9]. - * - * @throws IllegalArgumentException if name contains an illegal character - * - * @hide - */ - public LocationProvider(String name, ProviderProperties properties) { - if (name.matches(BAD_CHARS_REGEX)) { - throw new IllegalArgumentException("provider name contains illegal character: " + name); - } + LocationProvider(String name, ProviderProperties properties) { mName = name; mProperties = properties; } diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index 0346010f4587..f421029909bd 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -227,6 +227,44 @@ public class MediaMetadataRetriever implements AutoCloseable { public native String extractMetadata(int keyCode); /** + * This method is similar to {@link #getFrameAtTime(long, int, BitmapParams)} + * except that the device will choose the actual {@link Bitmap.Config} to use. + * + * @param timeUs The time position where the frame will be retrieved. + * When retrieving the frame at the given time position, there is no + * guarantee that the data source has a frame located at the position. + * When this happens, a frame nearby will be returned. If timeUs is + * negative, time position and option will ignored, and any frame + * that the implementation considers as representative may be returned. + * + * @param option a hint on how the frame is found. Use + * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame + * that has a timestamp earlier than or the same as timeUs. Use + * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame + * that has a timestamp later than or the same as timeUs. Use + * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame + * that has a timestamp closest to or the same as timeUs. Use + * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may + * or may not be a sync frame but is closest to or the same as timeUs. + * {@link #OPTION_CLOSEST} often has larger performance overhead compared + * to the other options if there is no sync frame located at timeUs. + * + * @return A Bitmap containing a representative video frame, which can be null, + * if such a frame cannot be retrieved. {@link Bitmap#getConfig()} can + * be used to query the actual {@link Bitmap.Config}. + * + * @see {@link #getFrameAtTime(long, int, BitmapParams)} + */ + public Bitmap getFrameAtTime(long timeUs, @Option int option) { + if (option < OPTION_PREVIOUS_SYNC || + option > OPTION_CLOSEST) { + throw new IllegalArgumentException("Unsupported option: " + option); + } + + return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/, null); + } + + /** * Call this method after setDataSource(). This method finds a * representative frame close to the given time position by considering * the given option if possible, and returns it as a bitmap. @@ -255,16 +293,60 @@ public class MediaMetadataRetriever implements AutoCloseable { * {@link #OPTION_CLOSEST} often has larger performance overhead compared * to the other options if there is no sync frame located at timeUs. * + * @param params BitmapParams that controls the returned bitmap config + * (such as pixel formats). + * * @return A Bitmap containing a representative video frame, which * can be null, if such a frame cannot be retrieved. + * + * @see {@link #getFrameAtTime(long, int)} */ - public Bitmap getFrameAtTime(long timeUs, @Option int option) { + public Bitmap getFrameAtTime( + long timeUs, @Option int option, @NonNull BitmapParams params) { if (option < OPTION_PREVIOUS_SYNC || option > OPTION_CLOSEST) { throw new IllegalArgumentException("Unsupported option: " + option); } - return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/); + return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/, params); + } + + /** + * This method is similar to {@link #getScaledFrameAtTime(long, int, int, int, BitmapParams)} + * except that the device will choose the actual {@link Bitmap.Config} to use. + * + * @param timeUs The time position in microseconds where the frame will be retrieved. + * When retrieving the frame at the given time position, there is no + * guarantee that the data source has a frame located at the position. + * When this happens, a frame nearby will be returned. If timeUs is + * negative, time position and option will ignored, and any frame + * that the implementation considers as representative may be returned. + * + * @param option a hint on how the frame is found. Use + * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame + * that has a timestamp earlier than or the same as timeUs. Use + * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame + * that has a timestamp later than or the same as timeUs. Use + * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame + * that has a timestamp closest to or the same as timeUs. Use + * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may + * or may not be a sync frame but is closest to or the same as timeUs. + * {@link #OPTION_CLOSEST} often has larger performance overhead compared + * to the other options if there is no sync frame located at timeUs. + * + * @param dstWidth expected output bitmap width + * @param dstHeight expected output bitmap height + * @return A Bitmap containing a representative video frame, which can be null, + * if such a frame cannot be retrieved. {@link Bitmap#getConfig()} can + * be used to query the actual {@link Bitmap.Config}. + * @throws IllegalArgumentException if passed in invalid option or width by height + * is less than or equal to 0. + * @see {@link #getScaledFrameAtTime(long, int, int, int, BitmapParams)} + */ + public Bitmap getScaledFrameAtTime( + long timeUs, @Option int option, int dstWidth, int dstHeight) { + validate(option, dstWidth, dstHeight); + return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, null); } /** @@ -297,15 +379,23 @@ public class MediaMetadataRetriever implements AutoCloseable { * * @param dstWidth expected output bitmap width * @param dstHeight expected output bitmap height + * @param params BitmapParams that controls the returned bitmap config + * (such as pixel formats). + * * @return A Bitmap of size not larger than dstWidth by dstHeight containing a * scaled video frame, which can be null, if such a frame cannot be retrieved. * @throws IllegalArgumentException if passed in invalid option or width by height * is less than or equal to 0. + * @see {@link #getScaledFrameAtTime(long, int, int, int)} */ - public Bitmap getScaledFrameAtTime( - long timeUs, @Option int option, int dstWidth, int dstHeight) { - if (option < OPTION_PREVIOUS_SYNC || - option > OPTION_CLOSEST) { + public Bitmap getScaledFrameAtTime(long timeUs, @Option int option, + int dstWidth, int dstHeight, @NonNull BitmapParams params) { + validate(option, dstWidth, dstHeight); + return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, params); + } + + private void validate(@Option int option, int dstWidth, int dstHeight) { + if (option < OPTION_PREVIOUS_SYNC || option > OPTION_CLOSEST) { throw new IllegalArgumentException("Unsupported option: " + option); } if (dstWidth <= 0) { @@ -314,8 +404,6 @@ public class MediaMetadataRetriever implements AutoCloseable { if (dstHeight <= 0) { throw new IllegalArgumentException("Invalid height: " + dstHeight); } - - return _getFrameAtTime(timeUs, option, dstWidth, dstHeight); } /** @@ -365,10 +453,12 @@ public class MediaMetadataRetriever implements AutoCloseable { * @see #getFrameAtTime(long, int) */ public Bitmap getFrameAtTime() { - return _getFrameAtTime(-1, OPTION_CLOSEST_SYNC, -1 /*dst_width*/, -1 /*dst_height*/); + return _getFrameAtTime( + -1, OPTION_CLOSEST_SYNC, -1 /*dst_width*/, -1 /*dst_height*/, null); } - private native Bitmap _getFrameAtTime(long timeUs, int option, int width, int height); + private native Bitmap _getFrameAtTime( + long timeUs, int option, int width, int height, @Nullable BitmapParams params); public static final class BitmapParams { private Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888; diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index 18fd1a01cfd6..bc4bcebaf560 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -350,9 +350,10 @@ static jobject getBitmapFromVideoFrame( return jBitmap; } -static int getColorFormat(JNIEnv *env, jobject options) { +static int getColorFormat(JNIEnv *env, jobject options, + int defaultPreferred = HAL_PIXEL_FORMAT_RGBA_8888) { if (options == NULL) { - return HAL_PIXEL_FORMAT_RGBA_8888; + return defaultPreferred; } ScopedLocalRef<jobject> inConfig(env, env->GetObjectField(options, fields.inPreferredConfig)); @@ -383,7 +384,8 @@ static SkColorType setOutColorType(JNIEnv *env, int colorFormat, jobject options } static jobject android_media_MediaMetadataRetriever_getFrameAtTime( - JNIEnv *env, jobject thiz, jlong timeUs, jint option, jint dst_width, jint dst_height) + JNIEnv *env, jobject thiz, jlong timeUs, jint option, + jint dst_width, jint dst_height, jobject params) { ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d", (long long)timeUs, option, dst_width, dst_height); @@ -392,10 +394,13 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime( jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); return NULL; } + // For getFrameAtTime family of calls, default to HAL_PIXEL_FORMAT_RGB_565 + // to keep the behavior consistent with older releases + int colorFormat = getColorFormat(env, params, HAL_PIXEL_FORMAT_RGB_565); // Call native method to retrieve a video frame VideoFrame *videoFrame = NULL; - sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option); + sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option, colorFormat); // TODO: Using unsecurePointer() has some associated security pitfalls // (see declaration for details). // Either document why it is safe in this case or address the @@ -408,7 +413,9 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime( return NULL; } - return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height, kRGB_565_SkColorType); + SkColorType outColorType = setOutColorType(env, colorFormat, params); + + return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height, outColorType); } static jobject android_media_MediaMetadataRetriever_getImageAtIndex( @@ -739,7 +746,7 @@ static const JNINativeMethod nativeMethods[] = { (void *)android_media_MediaMetadataRetriever_setDataSourceFD}, {"_setDataSource", "(Landroid/media/MediaDataSource;)V", (void *)android_media_MediaMetadataRetriever_setDataSourceCallback}, - {"_getFrameAtTime", "(JIII)Landroid/graphics/Bitmap;", + {"_getFrameAtTime", "(JIIILandroid/media/MediaMetadataRetriever$BitmapParams;)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime}, { "_getImageAtIndex", diff --git a/mime/Android.bp b/mime/Android.bp index 8b2b05958b6f..23a8fbf5059c 100644 --- a/mime/Android.bp +++ b/mime/Android.bp @@ -60,7 +60,7 @@ java_genrule { tools: [ "soong_zip", ], - srcs: [":mime.types"], + srcs: [":mime.types.minimized"], out: ["mimemap-res.jar"], cmd: "mkdir $(genDir)/res/ && cp $(in) $(genDir)/res/ && $(location soong_zip) -C $(genDir) -o $(out) -D $(genDir)/res/", } @@ -73,42 +73,49 @@ java_genrule { tools: [ "soong_zip", ], - srcs: [":mime.types"], + srcs: [":mime.types.minimized"], out: ["mimemap-testing-res.jar"], cmd: "mkdir $(genDir)/testres/ && cp $(in) $(genDir)/testres/ && $(location soong_zip) -C $(genDir) -o $(out) -D $(genDir)/testres/", } -// Combination of all *mime.types resources. +// Combination of all *mime.types.minimized resources. filegroup { - name: "mime.types", + name: "mime.types.minimized", visibility: [ "//visibility:private", ], srcs: [ - ":debian.mime.types", - ":android.mime.types", - ":vendor.mime.types", + ":debian.mime.types.minimized", + ":android.mime.types.minimized", + ":vendor.mime.types.minimized", ], } -filegroup { - name: "android.mime.types", +java_genrule { + name: "android.mime.types.minimized", visibility: [ "//visibility:private", ], - path: "java-res/", + out: ["android.mime.types"], srcs: [ "java-res/android.mime.types", ], + // strip comments normalize whitepace drop empty lines + cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | grep ' ' > $(out)", } -filegroup { - name: "vendor.mime.types", +// Unlike the other *mime.types files, vendor.mime.types gets '?' prepended to +// every field so that its mappings will never overwrite earlier mappings by +// the other resource files. http://b/141842825 +java_genrule { + name: "vendor.mime.types.minimized", visibility: [ "//visibility:private", ], - path: "java-res/", + out: ["vendor.mime.types"], srcs: [ "java-res/vendor.mime.types", ], + // strip comments normalize whitepace drop empty lines prepend ? to fields that are missing it + cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | grep ' ' | awk '{for(i=1;i<=NF;i++) { sub(/^\\??/, \"?\", $$i); }; print}' > $(out)", } diff --git a/mime/java/android/content/type/DefaultMimeMapFactory.java b/mime/java/android/content/type/DefaultMimeMapFactory.java index 03b685df644e..11d20d4d6c80 100644 --- a/mime/java/android/content/type/DefaultMimeMapFactory.java +++ b/mime/java/android/content/type/DefaultMimeMapFactory.java @@ -23,11 +23,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.function.Function; -import java.util.regex.Pattern; /** * Creates the framework default {@link MimeMap}, a bidirectional mapping @@ -53,8 +51,6 @@ public class DefaultMimeMapFactory { return create(resourceName -> c.getResourceAsStream("/res/" + resourceName)); } - private static final Pattern SPLIT_PATTERN = Pattern.compile("\\s+"); - /** * Creates a {@link MimeMap} instance whose resources are loaded from the * InputStreams looked up in {@code resourceSupplier}. @@ -63,33 +59,43 @@ public class DefaultMimeMapFactory { */ public static MimeMap create(Function<String, InputStream> resourceSupplier) { MimeMap.Builder builder = MimeMap.builder(); - parseTypes(builder, true, resourceSupplier, "mime.types"); - parseTypes(builder, true, resourceSupplier, "android.mime.types"); - parseTypes(builder, false, resourceSupplier, "vendor.mime.types"); + // The files loaded here must be in minimized format with lines of the + // form "mime/type ext1 ext2 ext3", i.e. no comments, no empty lines, no + // leading/trailing whitespace and with a single space between entries on + // each line. See http://b/142267887 + // + // Note: the order here matters - later entries can overwrite earlier ones + // (except that vendor.mime.types entries are prefixed with '?' which makes + // them never overwrite). + parseTypes(builder, resourceSupplier, "debian.mime.types"); + parseTypes(builder, resourceSupplier, "android.mime.types"); + parseTypes(builder, resourceSupplier, "vendor.mime.types"); return builder.build(); } - private static void parseTypes(MimeMap.Builder builder, boolean allowOverwrite, + private static void parseTypes(MimeMap.Builder builder, Function<String, InputStream> resourceSupplier, String resourceName) { try (InputStream inputStream = Objects.requireNonNull(resourceSupplier.apply(resourceName)); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { String line; + List<String> specs = new ArrayList<>(10); // re-use for each line while ((line = reader.readLine()) != null) { - int commentPos = line.indexOf('#'); - if (commentPos >= 0) { - line = line.substring(0, commentPos); - } - line = line.trim(); - if (line.isEmpty()) { - continue; - } - List<String> specs = Arrays.asList(SPLIT_PATTERN.split(line)); - if (!allowOverwrite) { - // Pretend that the mimeType and each file extension listed in the line - // carries a "?" prefix, which means that it can add new mappings but - // not modify existing mappings (putIfAbsent() semantics). - specs = ensurePrefix("?", specs); - } + specs.clear(); + // Lines are of the form "mimeSpec extSpec extSpec[...]" with a single space + // separating them and no leading/trailing spaces and no empty lines. + int startIdx = 0; + do { + int endIdx = line.indexOf(' ', startIdx); + if (endIdx < 0) { + endIdx = line.length(); + } + String spec = line.substring(startIdx, endIdx); + if (spec.isEmpty()) { + throw new IllegalArgumentException("Malformed line: " + line); + } + specs.add(spec); + startIdx = endIdx + 1; // skip over the space + } while (startIdx < line.length()); builder.put(specs.get(0), specs.subList(1, specs.size())); } } catch (IOException | RuntimeException e) { @@ -97,15 +103,4 @@ public class DefaultMimeMapFactory { } } - private static List<String> ensurePrefix(String prefix, List<String> strings) { - List<String> result = new ArrayList<>(strings.size()); - for (String s : strings) { - if (!s.startsWith(prefix)) { - s = prefix + s; - } - result.add(s); - } - return result; - } - } diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java index 91b292666756..66be25b53a62 100644 --- a/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java +++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java @@ -18,9 +18,13 @@ package com.android.server.backup.encryption; import java.io.Closeable; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; /** Utility methods for dealing with Streams */ public class StreamUtils { + private static final int MAX_COPY_BUFFER_SIZE = 1024; // 1k copy buffer size. + /** * Close a Closeable and silently ignore any IOExceptions. * @@ -33,4 +37,28 @@ public class StreamUtils { // Silently ignore } } + + /** + * Copy data from an InputStream to an OutputStream upto a given number of bytes. + * + * @param in The source InputStream + * @param out The destination OutputStream + * @param limit The maximum number of bytes to copy + * @throws IOException Thrown if there is a problem performing the copy. + */ + public static void copyStream(InputStream in, OutputStream out, int limit) throws IOException { + int bufferSize = Math.min(MAX_COPY_BUFFER_SIZE, limit); + byte[] buffer = new byte[bufferSize]; + + int copied = 0; + while (copied < limit) { + int maxReadSize = Math.min(bufferSize, limit - copied); + int read = in.read(buffer, 0, maxReadSize); + if (read < 0) { + return; // Reached the stream end before the limit + } + out.write(buffer, 0, read); + copied += read; + } + } } diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java new file mode 100644 index 000000000000..0baec8b0a450 --- /dev/null +++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.encryption.tasks; + +import static com.android.internal.util.Preconditions.checkNotNull; +import static com.android.internal.util.Preconditions.checkState; + +import android.annotation.Nullable; +import android.app.backup.BackupTransport; +import android.content.Context; +import android.util.Slog; + +import com.android.server.backup.encryption.FullBackupDataProcessor; +import com.android.server.backup.encryption.StreamUtils; +import com.android.server.backup.encryption.client.CryptoBackupServer; +import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.security.SecureRandom; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +/** + * Accepts backup data from a {@link InputStream} and passes it to the encrypted full data backup + * path. + */ +public class EncryptedFullBackupDataProcessor implements FullBackupDataProcessor { + + private static final String TAG = "EncryptedFullBackupDP"; + + private final Context mContext; + private final ExecutorService mExecutorService; + private final CryptoBackupServer mCryptoBackupServer; + private final SecureRandom mSecureRandom; + private final RecoverableKeyStoreSecondaryKey mSecondaryKey; + private final String mPackageName; + + @Nullable private InputStream mInputStream; + @Nullable private PipedOutputStream mOutputStream; + @Nullable private EncryptedFullBackupTask mBackupTask; + @Nullable private Future<Void> mBackupTaskFuture; + @Nullable private FullBackupCallbacks mFullBackupCallbacks; + + public EncryptedFullBackupDataProcessor( + Context context, + ExecutorService executorService, + CryptoBackupServer cryptoBackupServer, + SecureRandom secureRandom, + RecoverableKeyStoreSecondaryKey secondaryKey, + String packageName) { + mContext = checkNotNull(context); + mExecutorService = checkNotNull(executorService); + mCryptoBackupServer = checkNotNull(cryptoBackupServer); + mSecureRandom = checkNotNull(secureRandom); + mSecondaryKey = checkNotNull(secondaryKey); + mPackageName = checkNotNull(packageName); + } + + @Override + public boolean initiate(InputStream inputStream) throws IOException { + checkState(mBackupTask == null, "initiate() twice"); + + this.mInputStream = inputStream; + mOutputStream = new PipedOutputStream(); + + mBackupTask = + EncryptedFullBackupTask.newInstance( + mContext, + mCryptoBackupServer, + mSecureRandom, + mSecondaryKey, + mPackageName, + new PipedInputStream(mOutputStream)); + + return true; + } + + @Override + public void start() { + checkState(mBackupTask != null, "start() before initiate()"); + mBackupTaskFuture = mExecutorService.submit(mBackupTask); + } + + @Override + public int pushData(int numBytes) { + checkState( + mBackupTaskFuture != null && mInputStream != null && mOutputStream != null, + "pushData() before start()"); + + // If the upload has failed then stop without pushing any more bytes. + if (mBackupTaskFuture.isDone()) { + Optional<Exception> exception = getTaskException(); + Slog.e(TAG, "Encrypted upload failed", exception.orElse(null)); + if (exception.isPresent()) { + reportNetworkFailureIfNecessary(exception.get()); + + if (exception.get().getCause() instanceof SizeQuotaExceededException) { + return BackupTransport.TRANSPORT_QUOTA_EXCEEDED; + } + } + + return BackupTransport.TRANSPORT_ERROR; + } + + try { + StreamUtils.copyStream(mInputStream, mOutputStream, numBytes); + } catch (IOException e) { + Slog.e(TAG, "IOException when processing backup", e); + return BackupTransport.TRANSPORT_ERROR; + } + + return BackupTransport.TRANSPORT_OK; + } + + @Override + public void cancel() { + checkState(mBackupTaskFuture != null && mBackupTask != null, "cancel() before start()"); + mBackupTask.cancel(); + closeStreams(); + } + + @Override + public int finish() { + checkState(mBackupTaskFuture != null, "finish() before start()"); + + // getTaskException() waits for the task to finish. We must close the streams first, which + // causes the task to finish, otherwise it will block forever. + closeStreams(); + Optional<Exception> exception = getTaskException(); + + if (exception.isPresent()) { + Slog.e(TAG, "Exception during encrypted full backup", exception.get()); + reportNetworkFailureIfNecessary(exception.get()); + + if (exception.get().getCause() instanceof SizeQuotaExceededException) { + return BackupTransport.TRANSPORT_QUOTA_EXCEEDED; + } + return BackupTransport.TRANSPORT_ERROR; + + } else { + if (mFullBackupCallbacks != null) { + mFullBackupCallbacks.onSuccess(); + } + + return BackupTransport.TRANSPORT_OK; + } + } + + private void closeStreams() { + StreamUtils.closeQuietly(mInputStream); + StreamUtils.closeQuietly(mOutputStream); + } + + @Override + public void handleCheckSizeRejectionZeroBytes() { + cancel(); + } + + @Override + public void handleCheckSizeRejectionQuotaExceeded() { + cancel(); + } + + @Override + public void handleSendBytesQuotaExceeded() { + cancel(); + } + + @Override + public void attachCallbacks(FullBackupCallbacks fullBackupCallbacks) { + this.mFullBackupCallbacks = fullBackupCallbacks; + } + + private void reportNetworkFailureIfNecessary(Exception exception) { + if (!(exception.getCause() instanceof SizeQuotaExceededException) + && mFullBackupCallbacks != null) { + mFullBackupCallbacks.onTransferFailed(); + } + } + + private Optional<Exception> getTaskException() { + if (mBackupTaskFuture != null) { + try { + mBackupTaskFuture.get(); + } catch (InterruptedException | ExecutionException e) { + return Optional.of(e); + } + } + return Optional.empty(); + } +} diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/StreamUtilsTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/StreamUtilsTest.java new file mode 100644 index 000000000000..a95e87e3a8b7 --- /dev/null +++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/StreamUtilsTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.encryption; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +@RunWith(RobolectricTestRunner.class) +public class StreamUtilsTest { + private static final int SOURCE_DATA_SIZE = 64; + + private byte[] mSourceData; + + private InputStream mSource; + private ByteArrayOutputStream mDestination; + + @Before + public void setUp() { + mSourceData = new byte[SOURCE_DATA_SIZE]; + for (byte i = 0; i < SOURCE_DATA_SIZE; i++) { + mSourceData[i] = i; + } + mSource = new ByteArrayInputStream(mSourceData); + mDestination = new ByteArrayOutputStream(); + } + + @Test + public void copyStream_copiesAllBytesIfAsked() throws IOException { + StreamUtils.copyStream(mSource, mDestination, mSourceData.length); + assertOutputHasBytes(mSourceData.length); + } + + @Test + public void copyStream_stopsShortIfAsked() throws IOException { + StreamUtils.copyStream(mSource, mDestination, mSourceData.length - 10); + assertOutputHasBytes(mSourceData.length - 10); + } + + @Test + public void copyStream_stopsShortIfAskedToCopyMoreThanAvailable() throws IOException { + StreamUtils.copyStream(mSource, mDestination, mSourceData.length + 10); + assertOutputHasBytes(mSourceData.length); + } + + private void assertOutputHasBytes(int count) { + byte[] output = mDestination.toByteArray(); + assertThat(output.length).isEqualTo(count); + for (int i = 0; i < count; i++) { + assertThat(output[i]).isEqualTo(mSourceData[i]); + } + } +} diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessorTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessorTest.java new file mode 100644 index 000000000000..675d03fb9869 --- /dev/null +++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessorTest.java @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.encryption.tasks; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.testng.Assert.assertThrows; + +import android.annotation.Nullable; +import android.app.backup.BackupTransport; +import android.platform.test.annotations.Presubmit; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.server.backup.encryption.FullBackupDataProcessor; +import com.android.server.backup.encryption.chunking.ProtoStore; +import com.android.server.backup.encryption.client.CryptoBackupServer; +import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey; +import com.android.server.backup.encryption.keys.TertiaryKeyManager; +import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto; +import com.android.server.backup.encryption.testing.QueuingNonAutomaticExecutorService; + +import com.google.common.io.ByteStreams; +import com.google.common.primitives.Bytes; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.SecureRandom; + +import javax.crypto.spec.SecretKeySpec; + +@RunWith(RobolectricTestRunner.class) +@Presubmit +@Config( + shadows = { + EncryptedFullBackupDataProcessorTest.ShadowEncryptedFullBackupTask.class, + }) +public class EncryptedFullBackupDataProcessorTest { + + private static final String KEY_GENERATOR_ALGORITHM = "AES"; + + private static final String TEST_PACKAGE = "com.example.app1"; + private static final byte[] TEST_DATA_1 = {1, 2, 3, 4}; + private static final byte[] TEST_DATA_2 = {5, 6, 7, 8}; + + private final RecoverableKeyStoreSecondaryKey mTestSecondaryKey = + new RecoverableKeyStoreSecondaryKey( + /*alias=*/ "test_key", + new SecretKeySpec( + new byte[] { + 1, 2, 3, + }, + KEY_GENERATOR_ALGORITHM)); + + private QueuingNonAutomaticExecutorService mExecutorService; + private FullBackupDataProcessor mFullBackupDataProcessor; + @Mock private FullBackupDataProcessor.FullBackupCallbacks mFullBackupCallbacks; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mExecutorService = new QueuingNonAutomaticExecutorService(); + mFullBackupDataProcessor = + new EncryptedFullBackupDataProcessor( + ApplicationProvider.getApplicationContext(), + mExecutorService, + mock(CryptoBackupServer.class), + new SecureRandom(), + mTestSecondaryKey, + TEST_PACKAGE); + } + + @After + public void tearDown() { + ShadowEncryptedFullBackupTask.reset(); + } + + @Test + public void initiate_callTwice_throws() throws Exception { + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(new byte[10])); + + assertThrows( + IllegalStateException.class, + () -> mFullBackupDataProcessor.initiate(new ByteArrayInputStream(new byte[10]))); + } + + @Test + public void pushData_writesDataToTask() throws Exception { + byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2); + + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData)); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + mFullBackupDataProcessor.pushData(TEST_DATA_2.length); + finishBackupTask(); + mFullBackupDataProcessor.finish(); + + byte[] result = ByteStreams.toByteArray(ShadowEncryptedFullBackupTask.sInputStream); + assertThat(result).isEqualTo(Bytes.concat(TEST_DATA_1, TEST_DATA_2)); + } + + @Test + public void pushData_noError_returnsOk() throws Exception { + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1)); + mFullBackupDataProcessor.start(); + int result = mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + finishBackupTask(); + mFullBackupDataProcessor.finish(); + + assertThat(result).isEqualTo(BackupTransport.TRANSPORT_OK); + } + + @Test + public void pushData_ioExceptionOnCopy_returnsError() throws Exception { + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1)); + mFullBackupDataProcessor.start(); + + // Close the stream so there's an IO error when the processor tries to write to it. + ShadowEncryptedFullBackupTask.sInputStream.close(); + int result = mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + + finishBackupTask(); + mFullBackupDataProcessor.finish(); + + assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR); + } + + @Test + public void pushData_exceptionDuringUpload_returnsError() throws Exception { + byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2); + + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData)); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + finishBackupTaskWithException(new IOException("Test exception")); + int result = mFullBackupDataProcessor.pushData(TEST_DATA_2.length); + + assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR); + } + + @Test + public void pushData_quotaExceptionDuringUpload_doesNotLogAndReturnsQuotaExceeded() + throws Exception { + mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks); + byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2); + + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData)); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + finishBackupTaskWithException(new SizeQuotaExceededException()); + int result = mFullBackupDataProcessor.pushData(TEST_DATA_2.length); + + assertThat(result).isEqualTo(BackupTransport.TRANSPORT_QUOTA_EXCEEDED); + + verify(mFullBackupCallbacks, never()).onSuccess(); + verify(mFullBackupCallbacks, never()) + .onTransferFailed(); // FullBackupSession will handle this. + } + + @Test + public void pushData_unexpectedEncryptedBackup_logs() throws Exception { + byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2); + + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData)); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + finishBackupTaskWithException(new GeneralSecurityException()); + int result = mFullBackupDataProcessor.pushData(TEST_DATA_2.length); + + assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR); + } + + @Test + public void pushData_permanentExceptionDuringUpload_callsErrorCallback() throws Exception { + mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks); + byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2); + + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData)); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + finishBackupTaskWithException(new IOException()); + mFullBackupDataProcessor.pushData(TEST_DATA_2.length); + + verify(mFullBackupCallbacks, never()).onSuccess(); + verify(mFullBackupCallbacks).onTransferFailed(); + } + + @Test + public void pushData_beforeInitiate_throws() { + assertThrows( + IllegalStateException.class, + () -> mFullBackupDataProcessor.pushData(/*numBytes=*/ 10)); + } + + @Test + public void cancel_cancelsTask() throws Exception { + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1)); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + mFullBackupDataProcessor.cancel(); + + assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue(); + } + + @Test + public void cancel_beforeInitiate_throws() { + assertThrows(IllegalStateException.class, () -> mFullBackupDataProcessor.cancel()); + } + + @Test + public void finish_noException_returnsTransportOk() throws Exception { + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1)); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + finishBackupTask(); + int result = mFullBackupDataProcessor.finish(); + + assertThat(result).isEqualTo(BackupTransport.TRANSPORT_OK); + } + + @Test + public void finish_exceptionDuringUpload_returnsTransportError() throws Exception { + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1)); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + finishBackupTaskWithException(new IOException("Test exception")); + int result = mFullBackupDataProcessor.finish(); + + assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR); + } + + @Test + public void finish_successfulBackup_callsSuccessCallback() throws Exception { + mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks); + + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1)); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + finishBackupTask(); + mFullBackupDataProcessor.finish(); + + verify(mFullBackupCallbacks).onSuccess(); + verify(mFullBackupCallbacks, never()).onTransferFailed(); + } + + @Test + public void finish_backupFailedWithPermanentError_callsErrorCallback() throws Exception { + mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks); + + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1)); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + finishBackupTaskWithException(new IOException()); + mFullBackupDataProcessor.finish(); + + verify(mFullBackupCallbacks, never()).onSuccess(); + verify(mFullBackupCallbacks).onTransferFailed(); + } + + @Test + public void finish_backupFailedWithQuotaException_doesNotCallbackAndReturnsQuotaExceeded() + throws Exception { + mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks); + + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1)); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + finishBackupTaskWithException(new SizeQuotaExceededException()); + int result = mFullBackupDataProcessor.finish(); + + assertThat(result).isEqualTo(BackupTransport.TRANSPORT_QUOTA_EXCEEDED); + verify(mFullBackupCallbacks, never()).onSuccess(); + verify(mFullBackupCallbacks, never()) + .onTransferFailed(); // FullBackupSession will handle this. + } + + @Test + public void finish_beforeInitiate_throws() { + assertThrows(IllegalStateException.class, () -> mFullBackupDataProcessor.finish()); + } + + @Test + public void handleCheckSizeRejectionZeroBytes_cancelsTask() throws Exception { + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(new byte[10])); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.handleCheckSizeRejectionZeroBytes(); + + assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue(); + } + + @Test + public void handleCheckSizeRejectionQuotaExceeded_cancelsTask() throws Exception { + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1)); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + mFullBackupDataProcessor.handleCheckSizeRejectionQuotaExceeded(); + + assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue(); + } + + @Test + public void handleSendBytesQuotaExceeded_cancelsTask() throws Exception { + mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1)); + mFullBackupDataProcessor.start(); + mFullBackupDataProcessor.pushData(TEST_DATA_1.length); + mFullBackupDataProcessor.handleSendBytesQuotaExceeded(); + + assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue(); + } + + private void finishBackupTask() { + mExecutorService.runNext(); + } + + private void finishBackupTaskWithException(Exception exception) { + ShadowEncryptedFullBackupTask.sOnCallException = exception; + finishBackupTask(); + } + + @Implements(EncryptedFullBackupTask.class) + public static class ShadowEncryptedFullBackupTask { + + private static InputStream sInputStream; + @Nullable private static Exception sOnCallException; + private static boolean sCancelled; + + public void __constructor__( + ProtoStore<ChunksMetadataProto.ChunkListing> chunkListingStore, + TertiaryKeyManager tertiaryKeyManager, + EncryptedBackupTask task, + InputStream inputStream, + String packageName, + SecureRandom secureRandom) { + sInputStream = inputStream; + } + + @Implementation + public Void call() throws Exception { + if (sOnCallException != null) { + throw sOnCallException; + } + + return null; + } + + @Implementation + public void cancel() { + sCancelled = true; + } + + public static void reset() { + sOnCallException = null; + sCancelled = false; + } + } +} diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/QueuingNonAutomaticExecutorService.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/QueuingNonAutomaticExecutorService.java new file mode 100644 index 000000000000..9d2272e29945 --- /dev/null +++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/QueuingNonAutomaticExecutorService.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.encryption.testing; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * ExecutorService which needs to be stepped through the jobs in its' queue. + * + * <p>This is a deliberately simple implementation because it's only used in testing. The queued + * jobs are run on the main thread to eliminate any race condition bugs. + */ +public class QueuingNonAutomaticExecutorService extends AbstractExecutorService { + + private List<Runnable> mWaitingJobs = new ArrayList<>(); + private int mWaitingJobCount = 0; + + @Override + public void shutdown() { + mWaitingJobCount = mWaitingJobs.size(); + mWaitingJobs = null; // This will force an error if jobs are submitted after shutdown + } + + @Override + public List<Runnable> shutdownNow() { + List<Runnable> queuedJobs = mWaitingJobs; + shutdown(); + return queuedJobs; + } + + @Override + public boolean isShutdown() { + return mWaitingJobs == null; + } + + @Override + public boolean isTerminated() { + return mWaitingJobs == null && mWaitingJobCount == 0; + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + long expiry = System.currentTimeMillis() + unit.toMillis(timeout); + for (Runnable job : mWaitingJobs) { + if (System.currentTimeMillis() > expiry) { + return false; + } + + job.run(); + } + return true; + } + + @Override + public void execute(Runnable command) { + mWaitingJobs.add(command); + } + + public void runNext() { + if (mWaitingJobs.isEmpty()) { + throw new IllegalStateException("Attempted to run jobs on an empty paused executor"); + } + + mWaitingJobs.remove(0).run(); + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java index c205bb49228c..53a88a9a54e9 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java @@ -20,6 +20,7 @@ import android.service.notification.StatusBarNotification; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.logging.NotifLog; import javax.inject.Inject; import javax.inject.Singleton; @@ -34,8 +35,8 @@ import javax.inject.Singleton; public class CarNotificationEntryManager extends NotificationEntryManager { @Inject - public CarNotificationEntryManager(NotificationData notificationData) { - super(notificationData); + public CarNotificationEntryManager(NotificationData notificationData, NotifLog notifLog) { + super(notificationData, notifLog); } @Override diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index cc6e84259893..681d8f543575 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -108,6 +108,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.AutoHideController; @@ -296,7 +297,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt NotificationListener notificationListener, ConfigurationController configurationController, StatusBarWindowController statusBarWindowController, - StatusBarWindowViewController.Builder statusBarWindowViewControllerBuild) { + StatusBarWindowViewController.Builder statusBarWindowViewControllerBuild, + NotifLog notifLog) { super( lightBarController, autoHideController, @@ -350,7 +352,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt notificationListener, configurationController, statusBarWindowController, - statusBarWindowViewControllerBuild); + statusBarWindowViewControllerBuild, + notifLog); mNavigationBarController = navigationBarController; } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index c05c4cdf72d7..de6a3a8840a4 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -24,7 +24,6 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.content.pm.Signature; import android.os.Binder; import android.os.Build; @@ -49,7 +48,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; -import com.android.server.LocalServices; import libcore.io.IoUtils; @@ -1175,9 +1173,8 @@ final class SettingsState { } // If SetupWizard, done. - PackageManagerInternal packageManagerInternal = LocalServices.getService( - PackageManagerInternal.class); - if (packageName.equals(packageManagerInternal.getSetupWizardPackageName())) { + String setupWizPackage = context.getPackageManager().getSetupWizardPackageName(); + if (packageName.equals(setupWizPackage)) { sSystemUids.put(uid, uid); return true; } diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index d71d00968162..a0972498ab2a 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -381,6 +381,7 @@ public class BugreportProgressService extends Service { private final class BugreportCallbackImpl extends BugreportCallback { + @GuardedBy("mLock") private final BugreportInfo mInfo; BugreportCallbackImpl(BugreportInfo info) { @@ -389,10 +390,12 @@ public class BugreportProgressService extends Service { @Override public void onProgress(float progress) { - if (progress == 0) { - trackInfoWithId(); + synchronized (mLock) { + if (progress == 0) { + trackInfoWithIdLocked(); + } + checkProgressUpdatedLocked(mInfo, (int) progress); } - checkProgressUpdated(mInfo, (int) progress); } /** @@ -401,18 +404,21 @@ public class BugreportProgressService extends Service { */ @Override public void onError(@BugreportErrorCode int errorCode) { - trackInfoWithId(); - stopProgress(mInfo.id); + synchronized (mLock) { + trackInfoWithIdLocked(); + stopProgressLocked(mInfo.id); + } Log.e(TAG, "Bugreport API callback onError() errorCode = " + errorCode); return; } @Override public void onFinished() { - // TODO: Make all callback functions lock protected. mInfo.renameBugreportFile(); mInfo.renameScreenshots(mScreenshotsDir); - sendBugreportFinishedBroadcast(); + synchronized (mLock) { + sendBugreportFinishedBroadcastLocked(); + } } /** @@ -421,7 +427,8 @@ public class BugreportProgressService extends Service { * when dumpstate calls one of the callback functions (onProgress, onFinished, onError) * after the id has been incremented. */ - private void trackInfoWithId() { + @GuardedBy("mLock") + private void trackInfoWithIdLocked() { final int id = SystemProperties.getInt(PROPERTY_LAST_ID, 1); if (mBugreportInfos.get(id) == null) { mInfo.id = id; @@ -430,74 +437,75 @@ public class BugreportProgressService extends Service { return; } - private void sendBugreportFinishedBroadcast() { + @GuardedBy("mLock") + private void sendBugreportFinishedBroadcastLocked() { final String bugreportFilePath = mInfo.bugreportFile.getAbsolutePath(); if (mInfo.bugreportFile.length() == 0) { Log.e(TAG, "Bugreport file empty. File path = " + bugreportFilePath); return; } if (mInfo.type == BugreportParams.BUGREPORT_MODE_REMOTE) { - sendRemoteBugreportFinishedBroadcast(bugreportFilePath, mInfo.bugreportFile); + sendRemoteBugreportFinishedBroadcast(mContext, bugreportFilePath, + mInfo.bugreportFile); } else { - trackInfoWithId(); + trackInfoWithIdLocked(); cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE); final Intent intent = new Intent(INTENT_BUGREPORT_FINISHED); intent.putExtra(EXTRA_BUGREPORT, bugreportFilePath); - addScreenshotToIntent(intent); + addScreenshotToIntent(intent, mInfo); mContext.sendBroadcast(intent, android.Manifest.permission.DUMP); onBugreportFinished(mInfo.id); } } + } - private void sendRemoteBugreportFinishedBroadcast(String bugreportFileName, - File bugreportFile) { - cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE); - final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH); - final Uri bugreportUri = getUri(mContext, bugreportFile); - final String bugreportHash = generateFileHash(bugreportFileName); - if (bugreportHash == null) { - Log.e(TAG, "Error generating file hash for remote bugreport"); - return; - } - intent.setDataAndType(bugreportUri, BUGREPORT_MIMETYPE); - intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash); - intent.putExtra(EXTRA_BUGREPORT, bugreportFileName); - mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM, - android.Manifest.permission.DUMP); - } - - private void addScreenshotToIntent(Intent intent) { - final File screenshotFile = mInfo.screenshotFiles.isEmpty() - ? null : mInfo.screenshotFiles.get(0); - if (screenshotFile != null && screenshotFile.length() > 0) { - final String screenshotFilePath = screenshotFile.getAbsolutePath(); - intent.putExtra(EXTRA_SCREENSHOT, screenshotFilePath); - } - return; + private static void sendRemoteBugreportFinishedBroadcast(Context context, + String bugreportFileName, File bugreportFile) { + cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE); + final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH); + final Uri bugreportUri = getUri(context, bugreportFile); + final String bugreportHash = generateFileHash(bugreportFileName); + if (bugreportHash == null) { + Log.e(TAG, "Error generating file hash for remote bugreport"); } + intent.setDataAndType(bugreportUri, BUGREPORT_MIMETYPE); + intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash); + intent.putExtra(EXTRA_BUGREPORT, bugreportFileName); + context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, + android.Manifest.permission.DUMP); + } - private String generateFileHash(String fileName) { - String fileHash = null; - try { - MessageDigest md = MessageDigest.getInstance("SHA-256"); - FileInputStream input = new FileInputStream(new File(fileName)); - byte[] buffer = new byte[65536]; - int size; - while ((size = input.read(buffer)) > 0) { - md.update(buffer, 0, size); - } - input.close(); - byte[] hashBytes = md.digest(); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < hashBytes.length; i++) { - sb.append(String.format("%02x", hashBytes[i])); - } - fileHash = sb.toString(); - } catch (IOException | NoSuchAlgorithmException e) { - Log.e(TAG, "generating file hash for bugreport file failed " + fileName, e); + private static void addScreenshotToIntent(Intent intent, BugreportInfo info) { + final String screenshotFileName = info.name + ".png"; + final File screenshotFile = new File(BUGREPORT_DIR, screenshotFileName); + final String screenshotFilePath = screenshotFile.getAbsolutePath(); + if (screenshotFile.length() > 0) { + intent.putExtra(EXTRA_SCREENSHOT, screenshotFilePath); + } + return; + } + + private static String generateFileHash(String fileName) { + String fileHash = null; + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + FileInputStream input = new FileInputStream(new File(fileName)); + byte[] buffer = new byte[65536]; + int size; + while ((size = input.read(buffer)) > 0) { + md.update(buffer, 0, size); + } + input.close(); + byte[] hashBytes = md.digest(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < hashBytes.length; i++) { + sb.append(String.format("%02x", hashBytes[i])); } - return fileHash; + fileHash = sb.toString(); + } catch (IOException | NoSuchAlgorithmException e) { + Log.e(TAG, "generating file hash for bugreport file failed " + fileName, e); } + return fileHash; } static void cleanupOldFiles(final int minCount, final long minAge) { @@ -852,7 +860,8 @@ public class BugreportProgressService extends Service { /** * Finalizes the progress on a given bugreport and cancel its notification. */ - private void stopProgress(int id) { + @GuardedBy("mLock") + private void stopProgressLocked(int id) { if (mBugreportInfos.indexOfKey(id) < 0) { Log.w(TAG, "ID not watched: " + id); } else { @@ -883,7 +892,9 @@ public class BugreportProgressService extends Service { } deleteScreenshots(info); } - stopProgress(id); + synchronized (mLock) { + stopProgressLocked(id); + } } /** @@ -1178,7 +1189,9 @@ public class BugreportProgressService extends Service { if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) { Log.e(TAG, "Could not read bugreport file " + info.bugreportFile); Toast.makeText(context, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show(); - stopProgress(info.id); + synchronized (mLock) { + stopProgressLocked(info.id); + } return; } @@ -1290,7 +1303,9 @@ public class BugreportProgressService extends Service { final Intent sendIntent = buildSendIntent(mContext, info); if (sendIntent == null) { Log.w(TAG, "Stopping progres on ID " + id + " because share intent could not be built"); - stopProgress(id); + synchronized (mLock) { + stopProgressLocked(id); + } return; } @@ -1313,9 +1328,10 @@ public class BugreportProgressService extends Service { } else { mContext.startActivity(notifIntent); } - - // ... and stop watching this process. - stopProgress(id); + synchronized (mLock) { + // ... and stop watching this process. + stopProgressLocked(id); + } } static void sendShareIntent(Context context, Intent intent) { @@ -2361,14 +2377,18 @@ public class BugreportProgressService extends Service { // The right, long-term solution is to provide an onFinished() callback // on IDumpstateListener and call it instead of using a broadcast. Log.w(TAG, "Dumpstate process died:\n" + info); - stopProgress(info.id); + synchronized (mLock) { + stopProgressLocked(info.id); + } } token.asBinder().unlinkToDeath(this, 0); } @Override public void onProgress(int progress) throws RemoteException { - checkProgressUpdated(info, progress); + synchronized (mLock) { + checkProgressUpdatedLocked(info, progress); + } } @Override @@ -2387,7 +2407,8 @@ public class BugreportProgressService extends Service { } - private void checkProgressUpdated(BugreportInfo info, int progress) { + @GuardedBy("mLock") + private void checkProgressUpdatedLocked(BugreportInfo info, int progress) { if (progress > CAPPED_PROGRESS) { progress = CAPPED_PROGRESS; } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 403e894a68e4..b288eb7b5070 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -243,6 +243,9 @@ <!-- Permission to change the display color --> <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> + <!-- Query all packages on device on R+ --> + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> + <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java index f9ffb804f9f3..5010f319f8b4 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java @@ -43,6 +43,7 @@ public class AssistHandleViewController implements NavigationBarTransitions.Dark private Handler mHandler; private CornerHandleView mAssistHintLeft; private CornerHandleView mAssistHintRight; + private int mBottomOffset; @VisibleForTesting boolean mAssistHintVisible; @@ -62,6 +63,23 @@ public class AssistHandleViewController implements NavigationBarTransitions.Dark } /** + * Set the bottom offset. + * + * @param bottomOffset the bottom offset to translate. + */ + public void setBottomOffset(int bottomOffset) { + if (mBottomOffset != bottomOffset) { + mBottomOffset = bottomOffset; + if (mAssistHintVisible) { + // If assist handles are visible, hide them without animation and then make them + // show once again (with corrected bottom offset). + hideAssistHandles(); + setAssistHintVisible(true); + } + } + } + + /** * Controls the visibility of the assist gesture handles. * * @param visible whether the handles should be shown @@ -126,7 +144,8 @@ public class AssistHandleViewController implements NavigationBarTransitions.Dark xDirection * translationStart * view.getWidth(), xDirection * translationEnd * view.getWidth()); Animator translateY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, - translationStart * view.getHeight(), translationEnd * view.getHeight()); + translationStart * view.getHeight() + mBottomOffset, + translationEnd * view.getHeight() + mBottomOffset); AnimatorSet set = new AnimatorSet(); set.play(scaleX).with(scaleY); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 824034507019..cc548d0c7f8e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -569,7 +569,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (mStackView != null) { mStackView.updateDotVisibility(entry.key); } - mNotificationEntryManager.updateNotifications(); + mNotificationEntryManager.updateNotifications( + "BubbleController.onNotificationRemoveRequested"); return true; } else if (!userRemovedNotif && entry != null) { // This wasn't a user removal so we should remove the bubble as well @@ -609,7 +610,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mBubbleData.addSummaryToSuppress(summary.notification.getGroupKey(), summary.key); // Tell shade to update for the suppression - mNotificationEntryManager.updateNotifications(); + mNotificationEntryManager.updateNotifications( + "BubbleController.handleSummaryRemovalInterception"); } return !isAutogroupSummary; } else { @@ -760,7 +762,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mStackView.setExpanded(true); } - mNotificationEntryManager.updateNotifications(); + mNotificationEntryManager.updateNotifications( + "BubbleData.Listener.applyUpdate"); updateStack(); if (DEBUG_BUBBLE_CONTROLLER) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java index 6c0f90a65ae9..c4de2d3572bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java @@ -127,7 +127,7 @@ public class NotificationListener extends NotificationListenerWithPlugins { mEntryManager.removeNotification(key, rankingMap, UNDEFINED_DISMISS_REASON); } else { mEntryManager.getNotificationData() - .updateRanking(rankingMap); + .updateRanking(rankingMap, "onNotificationPosted"); } return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 4ba111436048..6ffea79c420a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -106,7 +106,7 @@ public class NotificationLockscreenUserManagerImpl implements isCurrentProfile(getSendingUserId())) { mUsersAllowingPrivateNotifications.clear(); updateLockscreenNotificationSetting(); - getEntryManager().updateNotifications(); + getEntryManager().updateNotifications("ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED"); } } }; @@ -124,7 +124,7 @@ public class NotificationLockscreenUserManagerImpl implements updatePublicMode(); // The filtering needs to happen before the update call below in order to make sure // the presenter has the updated notifications from the new user - getEntryManager().getNotificationData().filterAndSort(); + getEntryManager().getNotificationData().filterAndSort("user switched"); mPresenter.onUserSwitched(mCurrentUserId); for (UserChangedListener listener : mListeners) { @@ -205,7 +205,8 @@ public class NotificationLockscreenUserManagerImpl implements mUsersAllowingNotifications.clear(); // ... and refresh all the notifications updateLockscreenNotificationSetting(); - getEntryManager().updateNotifications(); + getEntryManager().updateNotifications("LOCK_SCREEN_SHOW_NOTIFICATIONS," + + " or LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS change"); } }; @@ -214,7 +215,8 @@ public class NotificationLockscreenUserManagerImpl implements public void onChange(boolean selfChange) { updateLockscreenNotificationSetting(); if (mDeviceProvisionedController.isDeviceProvisioned()) { - getEntryManager().updateNotifications(); + getEntryManager().updateNotifications("LOCK_SCREEN_ALLOW_REMOTE_INPUT" + + " or ZEN_MODE change"); } } }; @@ -532,7 +534,7 @@ public class NotificationLockscreenUserManagerImpl implements setLockscreenPublicMode(isProfilePublic, userId); mUsersWithSeperateWorkChallenge.put(userId, needsSeparateChallenge); } - getEntryManager().updateNotifications(); + getEntryManager().updateNotifications("NotificationLockscreenUserManager.updatePublicMode"); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index c50fb3d76b51..3616b541741c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -361,7 +361,7 @@ public class NotificationMediaManager implements Dumpable { } if (metaDataChanged) { - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("NotificationMediaManager - metaDataChanged"); } dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 148a1a87f305..01c79b3df064 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -40,6 +40,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRowBinder; +import com.android.systemui.statusbar.notification.logging.NotifEvent; +import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationContentInflater; import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; @@ -92,6 +94,7 @@ public class NotificationEntryManager implements private NotificationListenerService.RankingMap mLatestRankingMap; @VisibleForTesting protected NotificationData mNotificationData; + private NotifLog mNotifLog; @VisibleForTesting final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders @@ -123,8 +126,9 @@ public class NotificationEntryManager implements } @Inject - public NotificationEntryManager(NotificationData notificationData) { + public NotificationEntryManager(NotificationData notificationData, NotifLog notifLog) { mNotificationData = notificationData; + mNotifLog = notifLog; } /** Adds a {@link NotificationEntryListener}. */ @@ -178,7 +182,7 @@ public class NotificationEntryManager implements @Override public void onReorderingAllowed() { - updateNotifications(); + updateNotifications("reordering is now allowed"); } /** @@ -203,15 +207,19 @@ public class NotificationEntryManager implements return NotificationVisibility.obtain(key, rank, count, true, location); } - private void abortExistingInflation(String key) { + private void abortExistingInflation(String key, String reason) { if (mPendingNotifications.containsKey(key)) { NotificationEntry entry = mPendingNotifications.get(key); entry.abortTask(); mPendingNotifications.remove(key); + mNotifLog.log(NotifEvent.INFLATION_ABORTED, entry.sbn(), null, + "PendingNotification aborted. " + reason); } NotificationEntry addedEntry = mNotificationData.get(key); if (addedEntry != null) { addedEntry.abortTask(); + mNotifLog.log(NotifEvent.INFLATION_ABORTED, addedEntry.sbn(), + null, reason); } } @@ -247,7 +255,7 @@ public class NotificationEntryManager implements for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onBeforeNotificationAdded(entry); } - updateNotifications(); + updateNotifications("onAsyncInflationFinished"); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onNotificationAdded(entry); } @@ -276,7 +284,8 @@ public class NotificationEntryManager implements if (mRemoveInterceptor != null && mRemoveInterceptor.onNotificationRemoveRequested(key, reason)) { - // Remove intercepted; skip + // Remove intercepted; log and skip + mNotifLog.log(NotifEvent.REMOVE_INTERCEPTED); return; } @@ -291,13 +300,17 @@ public class NotificationEntryManager implements if (extender.shouldExtendLifetimeForPendingNotification(pendingEntry)) { extendLifetime(pendingEntry, extender); lifetimeExtended = true; + mNotifLog.log( + NotifEvent.LIFETIME_EXTENDED, + pendingEntry.sbn(), + "pendingEntry extendedBy=" + extender.toString()); } } } } if (!lifetimeExtended) { - abortExistingInflation(key); + abortExistingInflation(key, "removeNotification"); } if (entry != null) { @@ -310,6 +323,10 @@ public class NotificationEntryManager implements mLatestRankingMap = ranking; extendLifetime(entry, extender); lifetimeExtended = true; + mNotifLog.log( + NotifEvent.LIFETIME_EXTENDED, + entry.sbn(), + "entry extendedBy=" + extender.toString()); break; } } @@ -329,10 +346,12 @@ public class NotificationEntryManager implements handleGroupSummaryRemoved(key); mNotificationData.remove(key, ranking); - updateNotifications(); + updateNotifications("removeNotificationInternal"); Dependency.get(LeakDetector.class).trackGarbage(entry); removedByUser |= entryDismissed; + mNotifLog.log(NotifEvent.NOTIF_REMOVED, entry.sbn(), + "removedByUser=" + removedByUser); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onEntryRemoved(entry, visibility, removedByUser); } @@ -389,7 +408,7 @@ public class NotificationEntryManager implements Log.d(TAG, "addNotification key=" + key); } - mNotificationData.updateRanking(rankingMap); + mNotificationData.updateRanking(rankingMap, "addNotificationInternal"); Ranking ranking = new Ranking(); rankingMap.getRanking(key, ranking); @@ -400,9 +419,9 @@ public class NotificationEntryManager implements requireBinder().inflateViews(entry, () -> performRemoveNotification(notification, REASON_CANCEL)); - abortExistingInflation(key); - + abortExistingInflation(key, "addNotification"); mPendingNotifications.put(key, entry); + mNotifLog.log(NotifEvent.NOTIF_ADDED, entry.sbn()); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onPendingEntryAdded(entry); } @@ -423,7 +442,7 @@ public class NotificationEntryManager implements if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); final String key = notification.getKey(); - abortExistingInflation(key); + abortExistingInflation(key, "updateNotification"); NotificationEntry entry = mNotificationData.get(key); if (entry == null) { return; @@ -433,15 +452,15 @@ public class NotificationEntryManager implements // to keep its lifetime extended. cancelLifetimeExtension(entry); - mNotificationData.update(entry, ranking, notification); - + mNotificationData.update(entry, ranking, notification, "updateNotificationInternal"); + mNotifLog.log(NotifEvent.NOTIF_UPDATED, entry.sbn(), entry.ranking()); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onPreEntryUpdated(entry); } requireBinder().inflateViews(entry, () -> performRemoveNotification(notification, REASON_CANCEL)); - updateNotifications(); + updateNotifications("updateNotificationInternal"); if (DEBUG) { // Is this for you? @@ -465,8 +484,12 @@ public class NotificationEntryManager implements } } - public void updateNotifications() { - mNotificationData.filterAndSort(); + /** + * Update the notifications + * @param reason why the notifications are updating + */ + public void updateNotifications(String reason) { + mNotificationData.filterAndSort(reason); if (mPresenter != null) { mPresenter.updateNotificationViews(); } @@ -489,7 +512,7 @@ public class NotificationEntryManager implements } // Populate notification entries from the new rankings. - mNotificationData.updateRanking(rankingMap); + mNotificationData.updateRanking(rankingMap, "updateNotificationRanking"); updateRankingOfPendingNotifications(rankingMap); // By comparing the old and new UI adjustments, reinflate the view accordingly. @@ -501,7 +524,7 @@ public class NotificationEntryManager implements NotificationUiAdjustment.extractFromNotificationEntry(entry)); } - updateNotifications(); + updateNotifications("updateNotificationRanking"); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onNotificationRankingUpdated(rankingMap); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java index 769cbb7b984c..970cbf9aa1e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java @@ -81,7 +81,7 @@ public class NotificationListController { new DeviceProvisionedListener() { @Override public void onDeviceProvisionedChanged() { - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("device provisioned changed"); } }; @@ -106,7 +106,7 @@ public class NotificationListController { if (foregroundKey != null) { mEntryManager .getNotificationData().updateAppOp(appOp, uid, pkg, foregroundKey, showIcon); - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("app opp changed pkg=" + pkg); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java index cf0fbbb5d9c1..a98fa664a357 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java @@ -35,6 +35,8 @@ import com.android.systemui.Dependency; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; +import com.android.systemui.statusbar.notification.logging.NotifEvent; +import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -73,10 +75,13 @@ public class NotificationData { private RankingMap mRankingMap; private final Ranking mTmpRanking = new Ranking(); private final boolean mUsePeopleFiltering; + private final NotifLog mNotifLog; @Inject - public NotificationData(NotificationSectionsFeatureManager sectionsFeatureManager) { + public NotificationData(NotificationSectionsFeatureManager sectionsFeatureManager, + NotifLog notifLog) { mUsePeopleFiltering = sectionsFeatureManager.isFilteringEnabled(); + mNotifLog = notifLog; } public void setHeadsUpManager(HeadsUpManager headsUpManager) { @@ -179,7 +184,7 @@ public class NotificationData { } mGroupManager.onEntryAdded(entry); - updateRankingAndSort(mRankingMap); + updateRankingAndSort(mRankingMap, "addEntry=" + entry.sbn()); } public NotificationEntry remove(String key, RankingMap ranking) { @@ -189,7 +194,7 @@ public class NotificationData { } if (removed == null) return null; mGroupManager.onEntryRemoved(removed); - updateRankingAndSort(ranking); + updateRankingAndSort(ranking, "removeEntry=" + removed.sbn()); return removed; } @@ -197,15 +202,19 @@ public class NotificationData { public void update( NotificationEntry entry, RankingMap ranking, - StatusBarNotification notification) { - updateRanking(ranking); + StatusBarNotification notification, + String reason) { + updateRanking(ranking, reason); final StatusBarNotification oldNotification = entry.notification; entry.setNotification(notification); mGroupManager.onEntryUpdated(entry, oldNotification); } - public void updateRanking(RankingMap ranking) { - updateRankingAndSort(ranking); + /** + * Update ranking and trigger a re-sort + */ + public void updateRanking(RankingMap ranking, String reason) { + updateRankingAndSort(ranking, reason); } public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) { @@ -352,7 +361,7 @@ public class NotificationData { return false; } - private void updateRankingAndSort(RankingMap rankingMap) { + private void updateRankingAndSort(RankingMap rankingMap, String reason) { if (rankingMap != null) { mRankingMap = rankingMap; synchronized (mEntries) { @@ -375,7 +384,7 @@ public class NotificationData { } } } - filterAndSort(); + filterAndSort(reason); } /** @@ -393,7 +402,11 @@ public class NotificationData { // TODO: This should not be public. Instead the Environment should notify this class when // anything changed, and this class should call back the UI so it updates itself. - public void filterAndSort() { + /** + * Filters and sorts the list of notification entries + */ + public void filterAndSort(String reason) { + mNotifLog.log(NotifEvent.FILTER_AND_SORT, reason); mSortedAndFiltered.clear(); synchronized (mEntries) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java index 60cf995ad8d1..e5571b64cc9e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java @@ -41,6 +41,8 @@ import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.NotificationClicker; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; +import com.android.systemui.statusbar.notification.logging.NotifEvent; +import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationContentInflater; @@ -72,6 +74,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { private final boolean mAllowLongPress; private final KeyguardBypassController mKeyguardBypassController; private final StatusBarStateController mStatusBarStateController; + private final NotifLog mNotifLog; private NotificationRemoteInputManager mRemoteInputManager; private NotificationPresenter mPresenter; @@ -85,12 +88,14 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { public NotificationRowBinderImpl(Context context, boolean allowLongPress, KeyguardBypassController keyguardBypassController, - StatusBarStateController statusBarStateController) { + StatusBarStateController statusBarStateController, + NotifLog notifLog) { mContext = context; mMessagingUtil = new NotificationMessagingUtil(context); mAllowLongPress = allowLongPress; mKeyguardBypassController = keyguardBypassController; mStatusBarStateController = statusBarStateController; + mNotifLog = notifLog; } private NotificationRemoteInputManager getRemoteInputManager() { @@ -143,6 +148,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { row -> { bindRow(entry, pmUser, sbn, row, onDismissRunnable); updateNotification(entry, pmUser, sbn, row); + mNotifLog.log(NotifEvent.INFLATED, sbn); }); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java index 2396d28bd46d..7703cbd94e1c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java @@ -30,9 +30,9 @@ import java.lang.annotation.RetentionPolicy; * and triaging purposes. */ public class NotifEvent extends RichEvent { - public static final int TOTAL_EVENT_TYPES = 8; - private StatusBarNotification mSbn; - private Ranking mRanking; + public static final int TOTAL_EVENT_TYPES = 11; + private final StatusBarNotification mSbn; + private final Ranking mRanking; /** * Creates a NotifEvent with an event type that matches with an index in the array @@ -44,9 +44,20 @@ public class NotifEvent extends RichEvent { public NotifEvent(int logLevel, int type, String reason, StatusBarNotification sbn, Ranking ranking) { super(logLevel, type, reason); - mSbn = sbn.clone(); - mRanking = new Ranking(); - mRanking.populate(ranking); + + if (sbn != null) { + mSbn = sbn.cloneLight(); + } else { + mSbn = null; + } + + if (ranking != null) { + mRanking = new Ranking(); + mRanking.populate(ranking); + } else { + mRanking = null; + } + mMessage += getExtraInfo(); } @@ -76,11 +87,14 @@ public class NotifEvent extends RichEvent { "NotifAdded", "NotifRemoved", "NotifUpdated", - "HeadsUpStarted", - "HeadsUpEnded", "Filter", "Sort", + "FilterAndSort", "NotifVisibilityChanged", + "LifetimeExtended", + "RemoveIntercepted", + "InflationAborted", + "Inflated" }; if (events.length != TOTAL_EVENT_TYPES) { @@ -135,8 +149,19 @@ public class NotifEvent extends RichEvent { } } - @IntDef({NOTIF_ADDED, NOTIF_REMOVED, NOTIF_UPDATED, HEADS_UP_STARTED, HEADS_UP_ENDED, FILTER, - SORT, NOTIF_VISIBILITY_CHANGED}) + @IntDef({NOTIF_ADDED, + NOTIF_REMOVED, + NOTIF_UPDATED, + FILTER, + SORT, + FILTER_AND_SORT, + NOTIF_VISIBILITY_CHANGED, + LIFETIME_EXTENDED, + REMOVE_INTERCEPTED, + INFLATION_ABORTED, + INFLATED + }) + /** * Types of NotifEvents */ @@ -145,9 +170,13 @@ public class NotifEvent extends RichEvent { public static final int NOTIF_ADDED = 0; public static final int NOTIF_REMOVED = 1; public static final int NOTIF_UPDATED = 2; - public static final int HEADS_UP_STARTED = 3; - public static final int HEADS_UP_ENDED = 4; - public static final int FILTER = 5; - public static final int SORT = 6; - public static final int NOTIF_VISIBILITY_CHANGED = 7; + public static final int FILTER = 3; + public static final int SORT = 4; + public static final int FILTER_AND_SORT = 5; + public static final int NOTIF_VISIBILITY_CHANGED = 6; + public static final int LIFETIME_EXTENDED = 7; + // unable to remove notif - removal intercepted by {@link NotificationRemoveInterceptor} + public static final int REMOVE_INTERCEPTED = 8; + public static final int INFLATION_ABORTED = 9; + public static final int INFLATED = 10; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java index d42cd82f9784..8466d2e83002 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java @@ -82,6 +82,14 @@ public class NotifLog extends SysuiLog { } /** + * Logs a {@link NotifEvent} with a notification + * @return true if successfully logged, else false + */ + public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn, String msg) { + return log(eventType, sbn, null, msg); + } + + /** * Logs a {@link NotifEvent} with a ranking * @return true if successfully logged, else false */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 9f4b026b3d38..924a3470b832 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -1447,6 +1447,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } setDismissed(fromAccessibility); if (mEntry.isClearable()) { + // TODO: beverlyt, log dismissal // TODO: track dismiss sentiment if (mOnDismissRunnable != null) { mOnDismissRunnable.run(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java index 73093c6f471f..37f63c9779f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java @@ -139,7 +139,8 @@ public class NotificationBlockingHelperManager { mBlockingHelperRow.setBlockingHelperShowing(false); if (mBlockingHelperRow.isAttachedToWindow()) { - Dependency.get(NotificationEntryManager.class).updateNotifications(); + Dependency.get(NotificationEntryManager.class).updateNotifications( + "dismissCurrentBlockingHelper"); } mBlockingHelperRow = null; return true; 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 f5705c5f643b..7bbe8188b402 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 @@ -5333,7 +5333,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd requestChildrenUpdate(); onUpdateRowStates(); - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("StatusBar state changed"); updateVisibility(); } @@ -6492,12 +6492,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) { - mStatusBar.requestNotificationUpdate(); + mStatusBar.requestNotificationUpdate("onGroupCreatedFromChildren"); } @Override public void onGroupsChanged() { - mStatusBar.requestNotificationUpdate(); + mStatusBar.requestNotificationUpdate("onGroupsChanged"); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index a1a47e1305f5..2aae5b1b9139 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -67,6 +67,7 @@ import com.android.systemui.DockedStackExistsListener; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.assist.AssistHandleViewController; import com.android.systemui.assist.AssistManager; import com.android.systemui.model.SysUiState; import com.android.systemui.recents.OverviewProxyService; @@ -75,6 +76,7 @@ import com.android.systemui.recents.RecentsOnboarding; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.WindowManagerWrapper; +import com.android.systemui.statusbar.NavigationBarController; import com.android.systemui.statusbar.policy.DeadZone; import com.android.systemui.statusbar.policy.KeyButtonDrawable; @@ -1198,6 +1200,19 @@ public class NavigationBarView extends FrameLayout implements // we're passing the insets onto the gesture handler since the back arrow is only // conditionally added and doesn't always get all the insets. mEdgeBackGestureHandler.setInsets(leftInset, rightInset); + + // this allows assist handle to be drawn outside its bound so that it can align screen + // bottom by translating its y position. + final boolean shouldClip = + !isGesturalMode(mNavBarMode) || insets.getSystemWindowInsetBottom() == 0; + setClipChildren(shouldClip); + setClipToPadding(shouldClip); + + AssistHandleViewController controller = Dependency.get(NavigationBarController.class) + .getAssistHandlerViewController(); + if (controller != null) { + controller.setBottomOffset(insets.getSystemWindowInsetBottom()); + } return super.onApplyWindowInsets(insets); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 6ce6dfaf3c20..2b80d2282661 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -210,6 +210,7 @@ import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; +import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -385,6 +386,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final BroadcastDispatcher mBroadcastDispatcher; private final ConfigurationController mConfigurationController; private final StatusBarWindowViewController.Builder mStatusBarWindowViewControllerBuilder; + private final NotifLog mNotifLog; // expanded notifications protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window @@ -585,7 +587,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onStrongAuthStateChanged(int userId) { super.onStrongAuthStateChanged(userId); - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("onStrongAuthStateChanged"); } }; private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); @@ -599,6 +601,7 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mPulsing; private final BubbleController mBubbleController; private final BubbleController.BubbleExpandListener mBubbleExpandListener; + private ActivityIntentHelper mActivityIntentHelper; @Override @@ -669,7 +672,8 @@ public class StatusBar extends SystemUI implements DemoMode, NotificationListener notificationListener, ConfigurationController configurationController, StatusBarWindowController statusBarWindowController, - StatusBarWindowViewController.Builder statusBarWindowViewControllerBuilder) { + StatusBarWindowViewController.Builder statusBarWindowViewControllerBuilder, + NotifLog notifLog) { mLightBarController = lightBarController; mAutoHideController = autoHideController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; @@ -723,10 +727,11 @@ public class StatusBar extends SystemUI implements DemoMode, mConfigurationController = configurationController; mStatusBarWindowController = statusBarWindowController; mStatusBarWindowViewControllerBuilder = statusBarWindowViewControllerBuilder; + mNotifLog = notifLog; mBubbleExpandListener = (isExpanding, key) -> { - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("onBubbleExpandChanged"); updateScrimController(); }; } @@ -1160,7 +1165,8 @@ public class StatusBar extends SystemUI implements DemoMode, mContext, mAllowNotificationLongPress, mKeyguardBypassController, - mStatusBarStateController); + mStatusBarStateController, + mNotifLog); mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel, mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController, @@ -1443,8 +1449,12 @@ public class StatusBar extends SystemUI implements DemoMode, return mZenController.areNotificationsHiddenInShade(); } - public void requestNotificationUpdate() { - mEntryManager.updateNotifications(); + /** + * Request a notification update + * @param reason why we're requesting a notification update + */ + public void requestNotificationUpdate(String reason) { + mEntryManager.updateNotifications(reason); } /** @@ -1685,7 +1695,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("onHeadsUpStateChanged"); if (isDozing() && isHeadsUp) { entry.setPulseSuppressed(false); mDozeServiceHost.fireNotificationPulse(entry); @@ -3566,7 +3576,7 @@ public class StatusBar extends SystemUI implements DemoMode, updateQsExpansionEnabled(); mKeyguardViewMediator.setDozing(mDozing); - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("onDozingChanged"); updateDozingState(); updateScrimController(); updateReportRejectedTouchVisibility(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 2798c6b56771..5a2b5e38222a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -33,6 +33,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -91,7 +92,6 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class BubbleControllerTest extends SysuiTestCase { - @Mock private NotificationEntryManager mNotificationEntryManager; @Mock @@ -223,13 +223,13 @@ public class BubbleControllerTest extends SysuiTestCase { mBubbleController.updateBubble(mRow.getEntry()); assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().key)); assertTrue(mBubbleController.hasBubbles()); - verify(mNotificationEntryManager).updateNotifications(); + verify(mNotificationEntryManager).updateNotifications(any()); verify(mBubbleStateChangeListener).onHasBubblesChanged(true); mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE); assertFalse(mStatusBarWindowController.getBubblesShowing()); assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().key)); - verify(mNotificationEntryManager, times(2)).updateNotifications(); + verify(mNotificationEntryManager, times(2)).updateNotifications(anyString()); verify(mBubbleStateChangeListener).onHasBubblesChanged(false); } @@ -257,16 +257,16 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testDismissStack() { mBubbleController.updateBubble(mRow.getEntry()); - verify(mNotificationEntryManager, times(1)).updateNotifications(); + verify(mNotificationEntryManager, times(1)).updateNotifications(any()); assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().key)); mBubbleController.updateBubble(mRow2.getEntry()); - verify(mNotificationEntryManager, times(2)).updateNotifications(); + verify(mNotificationEntryManager, times(2)).updateNotifications(any()); assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().key)); assertTrue(mBubbleController.hasBubbles()); mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE); assertFalse(mStatusBarWindowController.getBubblesShowing()); - verify(mNotificationEntryManager, times(3)).updateNotifications(); + verify(mNotificationEntryManager, times(3)).updateNotifications(any()); assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().key)); assertNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().key)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index a02764320b6c..0569c55981fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -25,6 +25,7 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -99,7 +100,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testLockScreenShowNotificationsChangeUpdatesNotifications() { mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); - verify(mEntryManager, times(1)).updateNotifications(); + verify(mEntryManager, times(1)).updateNotifications(anyString()); } @Test @@ -138,7 +139,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { public void testSettingsObserverUpdatesNotifications() { when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); mLockscreenUserManager.getSettingsObserverForTest().onChange(false); - verify(mEntryManager, times(1)).updateNotifications(); + verify(mEntryManager, times(1)).updateNotifications(anyString()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 866ea5112794..e52a25806086 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -28,6 +28,7 @@ import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; @@ -80,6 +81,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationData.K import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; +import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -146,7 +148,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase { private final CountDownLatch mCountDownLatch; TestableNotificationEntryManager() { - super(new NotificationData(mock(NotificationSectionsFeatureManager.class))); + super(new NotificationData(mock(NotificationSectionsFeatureManager.class), + mock(NotifLog.class)), mock(NotifLog.class)); mCountDownLatch = new CountDownLatch(1); } @@ -259,7 +262,9 @@ public class NotificationEntryManagerTest extends SysuiTestCase { NotificationRowBinderImpl notificationRowBinder = new NotificationRowBinderImpl(mContext, true, /* allowLongPress */ - mock(KeyguardBypassController.class), mock(StatusBarStateController.class)); + mock(KeyguardBypassController.class), + mock(StatusBarStateController.class), + mock(NotifLog.class)); notificationRowBinder.setUpWithPresenter( mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback); notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class)); @@ -350,7 +355,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { // Ensure that update callbacks happen in correct order InOrder order = inOrder(mEntryListener, notifData, mPresenter, mEntryListener); order.verify(mEntryListener).onPreEntryUpdated(mEntry); - order.verify(notifData).filterAndSort(); + order.verify(notifData).filterAndSort(anyString()); order.verify(mPresenter).updateNotificationViews(); order.verify(mEntryListener).onPostEntryUpdated(mEntry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java index 6d275419ee94..8207a041ef50 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java @@ -21,6 +21,7 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -43,6 +44,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; @@ -76,7 +78,7 @@ public class NotificationListControllerTest extends SysuiTestCase { // TODO: Remove this once EntryManager no longer needs to be mocked private NotificationData mNotificationData = new NotificationData(new NotificationSectionsFeatureManager( - new DeviceConfigProxyFake(), mContext)); + new DeviceConfigProxyFake(), mContext), mock(NotifLog.class)); private int mNextNotifId = 0; @@ -113,7 +115,7 @@ public class NotificationListControllerTest extends SysuiTestCase { @Test public void testCallUpdateNotificationsOnDeviceProvisionedChange() { mProvisionedListener.onDeviceProvisionedChanged(); - verify(mEntryManager).updateNotifications(); + verify(mEntryManager).updateNotifications(anyString()); } @Test @@ -133,8 +135,8 @@ public class NotificationListControllerTest extends SysuiTestCase { // THEN the app op is added to the entry assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); - // THEN updateNotifications() is called - verify(mEntryManager, times(1)).updateNotifications(); + // THEN updateNotifications(TEST) is called + verify(mEntryManager, times(1)).updateNotifications(anyString()); } @Test @@ -146,8 +148,8 @@ public class NotificationListControllerTest extends SysuiTestCase { // WHEN An unrelated notification gets a new app op mController.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true); - // THEN We never call updateNotifications() - verify(mEntryManager, never()).updateNotifications(); + // THEN We never call updateNotifications(TEST) + verify(mEntryManager, never()).updateNotifications(anyString()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java index 5fbacb1d7adf..59c76a8cbb77 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java @@ -79,6 +79,7 @@ import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.SbnBuilder; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; +import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; @@ -141,7 +142,7 @@ public class NotificationDataTest extends SysuiTestCase { when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true); mNotificationData = new TestableNotificationData( mock(NotificationSectionsFeatureManager.class)); - mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class)); + mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class), ""); mRow = new NotificationTestHelper(getContext()).createRow(); Dependency.get(InitController.class).executePostInitTasks(); } @@ -633,7 +634,7 @@ public class NotificationDataTest extends SysuiTestCase { public static class TestableNotificationData extends NotificationData { public TestableNotificationData(NotificationSectionsFeatureManager sectionsFeatureManager) { - super(sectionsFeatureManager); + super(sectionsFeatureManager, mock(NotifLog.class)); } public static final String OVERRIDE_RANK = "r"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java index 6d6439532912..cc89504cb54d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -62,7 +63,6 @@ import org.mockito.MockitoAnnotations; @org.junit.runner.RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class NotificationBlockingHelperManagerTest extends SysuiTestCase { - private NotificationBlockingHelperManager mBlockingHelperManager; private NotificationTestHelper mHelper; @@ -112,7 +112,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper()); assertTrue(mBlockingHelperManager.isBlockingHelperRowNull()); - verify(mEntryManager, times(0)).updateNotifications(); + verify(mEntryManager, times(0)).updateNotifications(anyString()); } @Test @@ -125,7 +125,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper()); assertTrue(mBlockingHelperManager.isBlockingHelperRowNull()); - verify(mEntryManager).updateNotifications(); + verify(mEntryManager).updateNotifications(anyString()); } @Test @@ -267,7 +267,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper()); assertTrue(mBlockingHelperManager.isBlockingHelperRowNull()); - verify(mEntryManager).updateNotifications(); + verify(mEntryManager).updateNotifications(anyString()); } @Test 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 98485b311d17..95e9e67cb830 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 @@ -56,6 +56,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -232,7 +233,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { mock(ShadeController.class), mock(NotificationLockscreenUserManager.class), new NotificationEntryManager(new NotificationData(mock( - NotificationSectionsFeatureManager.class))), + NotificationSectionsFeatureManager.class), mock(NotifLog.class)), + mock(NotifLog.class)), mock(DozeLog.class)); mNotificationStackScroller = mNotificationStackScrollLayout; mKeyguardStatusView = NotificationPanelViewTest.this.mKeyguardStatusView; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 914717c0266d..12e9be15354f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -117,6 +117,7 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -218,6 +219,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private NotificationIconAreaController mNotificationIconAreaController; @Mock private StatusBarWindowViewController.Builder mStatusBarWindowViewControllerBuilder; @Mock private StatusBarWindowViewController mStatusBarWindowViewController; + @Mock private NotifLog mNotifLog; @Before public void setup() throws Exception { @@ -339,7 +341,8 @@ public class StatusBarTest extends SysuiTestCase { mNotificationListener, configurationController, mStatusBarWindowController, - mStatusBarWindowViewControllerBuilder); + mStatusBarWindowViewControllerBuilder, + mNotifLog); // TODO: we should be able to call mStatusBar.start() and have all the below values // initialized automatically. mStatusBar.mContext = mContext; @@ -873,7 +876,7 @@ public class StatusBarTest extends SysuiTestCase { public static class TestableNotificationEntryManager extends NotificationEntryManager { public TestableNotificationEntryManager(NotificationData notificationData) { - super(notificationData); + super(notificationData, mock(NotifLog.class)); } public void setUpForTest(NotificationPresenter presenter, diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 3eef92fc0aa7..1d666adff561 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -33,6 +33,8 @@ import android.os.PersistableBundle; import android.util.ArraySet; import android.util.SparseArray; +import com.android.server.pm.PackageList; + import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index b158c32cb7b2..e0f60b43f8c8 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -579,6 +579,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // the set of network types that can only be enabled by system/sig apps private List mProtectedNetworks; + private Set<String> mWolSupportedInterfaces; + private TelephonyManager mTelephonyManager; private KeepaliveTracker mKeepaliveTracker; @@ -1055,6 +1057,10 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + mWolSupportedInterfaces = new ArraySet( + mContext.getResources().getStringArray( + com.android.internal.R.array.config_wakeonlan_supported_interfaces)); + mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mTethering = deps.makeTethering(mContext, mNMS, mStatsService, mPolicyManager, @@ -5600,6 +5606,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { updateProxy(newLp, oldLp); } + + updateWakeOnLan(newLp); + // TODO - move this check to cover the whole function if (!Objects.equals(newLp, oldLp)) { synchronized (networkAgent) { @@ -5770,6 +5779,10 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void updateWakeOnLan(@NonNull LinkProperties lp) { + lp.setWakeOnLanSupported(mWolSupportedInterfaces.contains(lp.getInterfaceName())); + } + private int getNetworkPermission(NetworkCapabilities nc) { if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) { return INetd.PERMISSION_SYSTEM; diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 35a06a977a58..09f62ff3fc56 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -3064,11 +3064,6 @@ public class LocationManagerService extends ILocationManager.Stub { try { LocationProvider oldProvider = getLocationProviderLocked(name); if (oldProvider != null) { - if (oldProvider.isMock()) { - throw new IllegalArgumentException( - "Provider \"" + name + "\" already exists"); - } - removeProviderLocked(oldProvider); } @@ -3093,7 +3088,7 @@ public class LocationManagerService extends ILocationManager.Stub { try { LocationProvider testProvider = getLocationProviderLocked(name); if (testProvider == null || !testProvider.isMock()) { - throw new IllegalArgumentException("Provider \"" + name + "\" unknown"); + return; } removeProviderLocked(testProvider); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7cbd1fcebb94..a450479cfc97 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -18353,7 +18353,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int getCurrentUserId() { - return mUserController.getCurrentUserIdLU(); + return mUserController.getCurrentUserId(); } @Override diff --git a/services/core/java/com/android/server/integrity/model/EvaluationOutcome.java b/services/core/java/com/android/server/integrity/model/EvaluationOutcome.java new file mode 100644 index 000000000000..dc30dc39f44a --- /dev/null +++ b/services/core/java/com/android/server/integrity/model/EvaluationOutcome.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.integrity.model; + +/** + * A class encapsulating the result from the evaluation engine after evaluating rules against app + * install metadata. + * + * <p>It contains the outcome effect (whether to allow or block the install), and the rule causing + * that effect. + */ +public final class EvaluationOutcome { + + public enum Effect { + ALLOW, + DENY + } + + private final Effect mEffect; + private final Rule mRule; + + private EvaluationOutcome(Effect effect, Rule rule) { + this.mEffect = effect; + this.mRule = rule; + } + + public Effect getEffect() { + return mEffect; + } + + public Rule getRule() { + return mRule; + } + + /** + * Create an ALLOW evaluation outcome. + * + * @return An evaluation outcome with ALLOW effect and empty rule. + */ + public static EvaluationOutcome allow() { + return new EvaluationOutcome(Effect.ALLOW, Rule.EMPTY); + } + + /** + * Create a DENY evaluation outcome. + * + * @param rule Rule causing the DENY effect. + * @return An evaluation outcome with DENY effect and rule causing that effect. + */ + public static EvaluationOutcome deny(Rule rule) { + return new EvaluationOutcome(Effect.DENY, rule); + } +} diff --git a/services/core/java/com/android/server/integrity/model/OpenFormula.java b/services/core/java/com/android/server/integrity/model/OpenFormula.java index 218cdc99c016..e9fa1a370611 100644 --- a/services/core/java/com/android/server/integrity/model/OpenFormula.java +++ b/services/core/java/com/android/server/integrity/model/OpenFormula.java @@ -16,9 +16,11 @@ package com.android.server.integrity.model; +import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; -import android.annotation.Nullable; +import java.util.Collections; +import java.util.List; /** * Represents a complex formula consisting of other simple and complex formulas. @@ -34,46 +36,33 @@ public final class OpenFormula extends Formula { } private final Connector mConnector; - private final Formula mMainFormula; - private final Formula mAuxiliaryFormula; + private final List<Formula> mFormulas; - public OpenFormula(Connector connector, Formula mainFormula, - @Nullable Formula auxiliaryFormula) { - validateAuxiliaryFormula(connector, auxiliaryFormula); + public OpenFormula(Connector connector, List<Formula> formulas) { + validateFormulas(connector, formulas); this.mConnector = checkNotNull(connector); - this.mMainFormula = checkNotNull(mainFormula); - // TODO: Add validators on auxiliary formula - this.mAuxiliaryFormula = auxiliaryFormula; + this.mFormulas = Collections.unmodifiableList(checkNotNull(formulas)); } public Connector getConnector() { return mConnector; } - public Formula getMainFormula() { - return mMainFormula; + public List<Formula> getFormulas() { + return mFormulas; } - public Formula getAuxiliaryFormula() { - return mAuxiliaryFormula; - } - - private void validateAuxiliaryFormula(Connector connector, Formula auxiliaryFormula) { - boolean validAuxiliaryFormula; + private void validateFormulas(Connector connector, List<Formula> formulas) { switch (connector) { case AND: case OR: - validAuxiliaryFormula = (auxiliaryFormula != null); + checkArgument(formulas.size() >= 2, + String.format("Connector %s must have at least 2 formulas", connector)); break; case NOT: - validAuxiliaryFormula = (auxiliaryFormula == null); + checkArgument(formulas.size() == 1, + String.format("Connector %s must have 1 formula only", connector)); break; - default: - validAuxiliaryFormula = false; - } - if (!validAuxiliaryFormula) { - throw new IllegalArgumentException( - String.format("Invalid formulas used for connector %s", connector)); } } } diff --git a/services/core/java/com/android/server/integrity/model/Rule.java b/services/core/java/com/android/server/integrity/model/Rule.java index 4fd40c1e0365..3d233abda5ed 100644 --- a/services/core/java/com/android/server/integrity/model/Rule.java +++ b/services/core/java/com/android/server/integrity/model/Rule.java @@ -25,7 +25,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; */ public final class Rule { - enum Effect { + public enum Effect { DENY } diff --git a/core/java/android/content/pm/PackageList.java b/services/core/java/com/android/server/pm/PackageList.java index e3eb2c55a2bb..60bc8a87f62d 100644 --- a/core/java/android/content/pm/PackageList.java +++ b/services/core/java/com/android/server/pm/PackageList.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package android.content.pm; +package com.android.server.pm; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal.PackageListObserver; import com.android.server.LocalServices; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index e0697a636498..c53b81081d2f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -162,7 +162,6 @@ import android.content.pm.PackageBackwardCompatibility; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstaller; -import android.content.pm.PackageList; import android.content.pm.PackageManager; import android.content.pm.PackageManager.LegacyPackageDeleteObserver; import android.content.pm.PackageManager.ModuleInfoFlags; @@ -3048,7 +3047,7 @@ public class PackageManagerService extends IPackageManager.Stub // Resolve protected action filters. Only the setup wizard is allowed to // have a high priority filter for these actions. - mSetupWizardPackage = getSetupWizardPackageName(); + mSetupWizardPackage = getSetupWizardPackageNameImpl(); mComponentResolver.fixProtectedFilterPriorities(); mSystemTextClassifierPackage = getSystemTextClassifierPackageName(); @@ -19652,7 +19651,7 @@ public class PackageManagerService extends IPackageManager.Stub set, comp, userId); } - private @Nullable String getSetupWizardPackageName() { + private @Nullable String getSetupWizardPackageNameImpl() { final Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_SETUP_WIZARD); @@ -19759,6 +19758,14 @@ public class PackageManagerService extends IPackageManager.Stub return systemCaptionsServiceComponentName.getPackageName(); } + @Override + public String getSetupWizardPackageName() { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Non-system caller"); + } + return mPmInternal.getSetupWizardPackageName(); + } + public String getIncidentReportApproverPackageName() { return mContext.getString(R.string.config_incidentReportApproverPackage); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 237a7712ce9b..c851cc69a732 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -4543,6 +4543,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void wakeUpFromPowerKey(long eventTime) { wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER"); + + // Turn on the connected TV and switch HDMI input if we're a HDMI playback device. + final HdmiControl hdmiControl = getHdmiControl(); + if (hdmiControl != null) { + hdmiControl.turnOnTv(); + } } private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason, @@ -4575,7 +4581,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // ... eventually calls finishWindowsDrawn which will finalize our screen turn on // as well as enabling the orientation change logic/sensor. mWindowManagerInternal.waitForAllWindowsDrawn(mWindowManagerDrawCallback, - WAITING_FOR_DRAWN_TIMEOUT); + WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY); } // Called on the DisplayManager's DisplayPowerController thread. diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 976fd52f17e5..4f52d9df817a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1723,25 +1723,27 @@ final class ActivityRecord extends AppWindowToken { // implied that the current finishing activity should be added into stopping list rather // than destroy immediately. final boolean isNextNotYetVisible = next != null && (!next.nowVisible || !next.visible); - final ActivityStack stack = getActivityStack(); - final boolean notFocusedStack = stack != mRootActivityContainer.getTopDisplayFocusedStack(); + final boolean notGlobalFocusedStack = + getActivityStack() != mRootActivityContainer.getTopDisplayFocusedStack(); if (isVisible && isNextNotYetVisible) { + // Add this activity to the list of stopping activities. It will be processed and + // destroyed when the next activity reports idle. addToStopping(false /* scheduleIdle */, false /* idleDelayed */, "completeFinishing"); - if (DEBUG_STATES) { - Slog.v(TAG_STATES, "Moving to STOPPING: " + this + " (finish requested)"); - } setState(STOPPING, "completeFinishing"); - if (notFocusedStack) { + if (notGlobalFocusedStack) { + // Ensuring visibility and configuration only for non-focused stacks since this + // method call is relatively expensive and not necessary for focused stacks. mRootActivityContainer.ensureVisibilityAndConfig(next, getDisplayId(), false /* markFrozenIfConfigChanged */, true /* deferResume */); } - } else if (isVisible && isState(PAUSED) && getActivityStack().isFocusedStackOnDisplay() - && !inPinnedWindowingMode()) { - // TODO(b/137329632): Currently non-focused stack is handled differently. - addToFinishingAndWaitForIdle(); + } else if (addToFinishingAndWaitForIdle()) { + // We added this activity to the finishing list and something else is becoming resumed. + // The activity will complete finishing when the next activity reports idle. No need to + // do anything else here. } else { - // Not waiting for the next one to become visible - finish right away. + // Not waiting for the next one to become visible, and nothing else will be resumed in + // place of this activity - requesting destruction right away. activityRemoved = destroyIfPossible(reason); } @@ -1798,13 +1800,20 @@ final class ActivityRecord extends AppWindowToken { return activityRemoved; } + /** + * Add this activity to the list of finishing and trigger resuming of activities in focused + * stacks. + * @return {@code true} if some other activity is being resumed as a result of this call. + */ @VisibleForTesting - void addToFinishingAndWaitForIdle() { + boolean addToFinishingAndWaitForIdle() { if (DEBUG_STATES) Slog.v(TAG, "Enqueueing pending finish: " + this); setState(FINISHING, "addToFinishingAndWaitForIdle"); - mStackSupervisor.mFinishingActivities.add(this); + if (!mStackSupervisor.mFinishingActivities.contains(this)) { + mStackSupervisor.mFinishingActivities.add(this); + } resumeKeyDispatchingLocked(); - mRootActivityContainer.resumeFocusedStacksTopActivities(); + return mRootActivityContainer.resumeFocusedStacksTopActivities(); } /** diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 0844323e08fa..f592ac6957cb 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -132,7 +132,6 @@ import static com.android.server.wm.WindowManagerService.dipToPixel; import static com.android.server.wm.WindowState.EXCLUSION_LEFT; import static com.android.server.wm.WindowState.EXCLUSION_RIGHT; import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP; -import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; import static com.android.server.wm.utils.RegionUtils.forEachRectReverse; import static com.android.server.wm.utils.RegionUtils.rectListToRegion; @@ -3519,19 +3518,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mWmService.mWindowPlacerLocked.performSurfacePlacement(); } - void waitForAllWindowsDrawn() { - final WindowManagerPolicy policy = mWmService.mPolicy; - forAllWindows(w -> { - final boolean keyguard = policy.isKeyguardHostWindow(w.mAttrs); - if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) { - w.mWinAnimator.mDrawState = DRAW_PENDING; - // Force add to mResizingWindows. - w.resetLastContentInsets(); - mWmService.mWaitingForDrawn.add(w); - } - }, true /* traverseTopToBottom */); - } - // TODO: Super crazy long method that should be broken down... void applySurfaceChangesTransaction(boolean recoveringMemory) { final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked; diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index ae3b5f2f70d3..d3e429019767 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -819,9 +819,12 @@ public class DockedStackDividerController { // We put all tasks into drag resizing mode - wait until all of them have completed the // drag resizing switch. - if (!mService.mWaitingForDrawn.isEmpty()) { - mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT); - mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, + final Runnable existingWaitingForDrwanCallback = + mService.mWaitingForDrawnCallbacks.get(mService.mRoot); + if (existingWaitingForDrwanCallback != null) { + mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, mService.mRoot); + mService.mH.sendMessageDelayed(mService.mH.obtainMessage(H.WAITING_FOR_DRAWN_TIMEOUT, + mService.mRoot), IME_ADJUST_DRAWN_TIMEOUT); mAnimationStartDelayed = true; if (imeWin != null) { @@ -838,10 +841,8 @@ public class DockedStackDividerController { // still gets executed. // TODO: Have a real system where we can wait on different windows to be drawn with // different callbacks. - if (mService.mWaitingForDrawnCallback != null) { - mService.mWaitingForDrawnCallback.run(); - } - mService.mWaitingForDrawnCallback = () -> { + existingWaitingForDrwanCallback.run(); + mService.mWaitingForDrawnCallbacks.put(mService.mRoot, () -> { synchronized (mService.mGlobalLock) { mAnimationStartDelayed = false; if (mDelayedImeWin != null) { @@ -863,7 +864,7 @@ public class DockedStackDividerController { notifyAdjustedForImeChanged( mAdjustedForIme || mAdjustedForDivider, duration); } - }; + }); } else { notifyAdjustedForImeChanged( adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION); diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java index 013607e65367..5d27390da588 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java +++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java @@ -17,7 +17,6 @@ package com.android.server.wm; import android.content.ComponentName; -import android.content.pm.PackageList; import android.content.pm.PackageManagerInternal; import android.graphics.Rect; import android.os.Environment; @@ -32,6 +31,7 @@ import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.server.LocalServices; +import com.android.server.pm.PackageList; import com.android.server.wm.LaunchParamsController.LaunchParams; import libcore.io.IoUtils; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 0c8cd4330d70..6f10d3d291b1 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -720,7 +720,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mUpdateRotation = updateRotationUnchecked(); } - if (mWmService.mWaitingForDrawnCallback != null + if (!mWmService.mWaitingForDrawnCallbacks.isEmpty() || (mOrientationChangeComplete && !isLayoutNeeded() && !mUpdateRotation)) { mWmService.checkDrawnWindowsLocked(); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 19fcb1b96e7b..a4ab66aef550 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -33,6 +33,7 @@ import static com.android.server.wm.WindowContainerProto.VISIBLE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import android.annotation.CallSuper; import android.annotation.IntDef; @@ -54,9 +55,11 @@ import android.view.SurfaceSession; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; +import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.SurfaceAnimator.Animatable; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedList; import java.util.function.Consumer; @@ -124,6 +127,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< private final Transaction mPendingTransaction; /** + * Windows that clients are waiting to have drawn. + */ + final ArrayList<WindowState> mWaitingForDrawn = new ArrayList<>(); + + /** * Applied as part of the animation pass in "prepareSurfaces". */ protected final SurfaceAnimator mSurfaceAnimator; @@ -1434,6 +1442,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } } + void waitForAllWindowsDrawn() { + final WindowManagerPolicy policy = mWmService.mPolicy; + forAllWindows(w -> { + final boolean keyguard = policy.isKeyguardHostWindow(w.mAttrs); + if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) { + w.mWinAnimator.mDrawState = DRAW_PENDING; + // Force add to mResizingWindows. + w.resetLastContentInsets(); + mWaitingForDrawn.add(w); + } + }, true /* traverseTopToBottom */); + } + Dimmer getDimmer() { if (mParent == null) { return null; diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index bb3caa9ed4ed..f4b76729b7ea 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -313,10 +313,15 @@ public abstract class WindowManagerInternal { public abstract void showGlobalActions(); /** - * Invalidate all visible windows. Then report back on the callback once all windows have - * redrawn. + * Invalidate all visible windows on a given display, and report back on the callback when all + * windows have redrawn. + * + * @param callback reporting callback to be called when all windows have redrawn. + * @param timeout calls the callback anyway after the timeout. + * @param displayId waits for the windows on the given display, INVALID_DISPLAY to wait for all + * windows on all displays. */ - public abstract void waitForAllWindowsDrawn(Runnable callback, long timeout); + public abstract void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId); /** * Overrides the display size. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 20812e98bf4a..0f4d0a8662f4 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -290,6 +290,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -568,13 +569,10 @@ public class WindowManagerService extends IWindowManager.Stub final ArrayList<WindowState> mForceRemoves = new ArrayList<>(); /** - * Windows that clients are waiting to have drawn. + * The callbacks to make when the windows all have been drawn for a given + * {@link WindowContainer}. */ - ArrayList<WindowState> mWaitingForDrawn = new ArrayList<>(); - /** - * And the callback to make when they've all been drawn. - */ - Runnable mWaitingForDrawnCallback; + final HashMap<WindowContainer, Runnable> mWaitingForDrawnCallbacks = new HashMap<>(); /** List of window currently causing non-system overlay windows to be hidden. */ private ArrayList<WindowState> mHidingNonSystemOverlayWindows = new ArrayList<>(); @@ -4736,12 +4734,12 @@ public class WindowManagerService extends IWindowManager.Stub case WAITING_FOR_DRAWN_TIMEOUT: { Runnable callback = null; + final WindowContainer container = (WindowContainer) msg.obj; synchronized (mGlobalLock) { ProtoLog.w(WM_ERROR, "Timeout waiting for drawn: undrawn=%s", - mWaitingForDrawn); - mWaitingForDrawn.clear(); - callback = mWaitingForDrawnCallback; - mWaitingForDrawnCallback = null; + container.mWaitingForDrawn); + container.mWaitingForDrawn.clear(); + callback = mWaitingForDrawnCallbacks.remove(container); } if (callback != null) { callback.run(); @@ -4773,9 +4771,9 @@ public class WindowManagerService extends IWindowManager.Stub } case ALL_WINDOWS_DRAWN: { Runnable callback; + final WindowContainer container = (WindowContainer) msg.obj; synchronized (mGlobalLock) { - callback = mWaitingForDrawnCallback; - mWaitingForDrawnCallback = null; + callback = mWaitingForDrawnCallbacks.remove(container); } if (callback != null) { callback.run(); @@ -5265,30 +5263,32 @@ public class WindowManagerService extends IWindowManager.Stub } void checkDrawnWindowsLocked() { - if (mWaitingForDrawn.isEmpty() || mWaitingForDrawnCallback == null) { + if (mWaitingForDrawnCallbacks.isEmpty()) { return; } - for (int j = mWaitingForDrawn.size() - 1; j >= 0; j--) { - WindowState win = mWaitingForDrawn.get(j); - ProtoLog.i(WM_DEBUG_SCREEN_ON, + mWaitingForDrawnCallbacks.forEach((container, callback) -> { + for (int j = container.mWaitingForDrawn.size() - 1; j >= 0; j--) { + final WindowState win = (WindowState) container.mWaitingForDrawn.get(j); + ProtoLog.i(WM_DEBUG_SCREEN_ON, "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d", win, win.mRemoved, win.isVisibleLw(), win.mHasSurface, win.mWinAnimator.mDrawState); - if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) { - // 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); - mWaitingForDrawn.remove(win); - } else if (win.hasDrawnLw()) { - // Window is now drawn (and shown). - ProtoLog.d(WM_DEBUG_SCREEN_ON, "Window drawn win=%s", win); - mWaitingForDrawn.remove(win); + if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) { + // 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()) { + // Window is now drawn (and shown). + ProtoLog.d(WM_DEBUG_SCREEN_ON, "Window drawn win=%s", win); + container.mWaitingForDrawn.remove(win); + } } - } - if (mWaitingForDrawn.isEmpty()) { - ProtoLog.d(WM_DEBUG_SCREEN_ON, "All windows drawn!"); - mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT); - mH.sendEmptyMessage(H.ALL_WINDOWS_DRAWN); - } + if (container.mWaitingForDrawn.isEmpty()) { + ProtoLog.d(WM_DEBUG_SCREEN_ON, "All windows drawn!"); + mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container); + mH.sendMessage(mH.obtainMessage(H.ALL_WINDOWS_DRAWN, container)); + } + }); } void setHoldScreenLocked(final Session newHoldScreen) { @@ -5934,13 +5934,18 @@ public class WindowManagerService extends IWindowManager.Stub } } } - if (mWaitingForDrawn.size() > 0) { + if (!mWaitingForDrawnCallbacks.isEmpty()) { pw.println(); pw.println(" Clients waiting for these windows to be drawn:"); - for (int i=mWaitingForDrawn.size()-1; i>=0; i--) { - WindowState win = mWaitingForDrawn.get(i); - pw.print(" Waiting #"); pw.print(i); pw.print(' '); pw.print(win); - } + mWaitingForDrawnCallbacks.forEach((wc, callback) -> { + pw.print(" WindowContainer "); + pw.println(wc.getName()); + for (int i = wc.mWaitingForDrawn.size() - 1; i >= 0; i--) { + final WindowState win = (WindowState) wc.mWaitingForDrawn.get(i); + pw.print(" Waiting #"); pw.print(i); pw.print(' '); pw.print(win); + } + }); + } pw.println(); pw.print(" mGlobalConfiguration="); pw.println(mRoot.getConfiguration()); @@ -7096,17 +7101,25 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void waitForAllWindowsDrawn(Runnable callback, long timeout) { + public void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId) { + final WindowContainer container = displayId == INVALID_DISPLAY + ? mRoot : mRoot.getDisplayContent(displayId); + if (container == null) { + // The waiting container doesn't exist, no need to wait to run the callback. Run and + // return; + callback.run(); + return; + } boolean allWindowsDrawn = false; synchronized (mGlobalLock) { - mWaitingForDrawnCallback = callback; - getDefaultDisplayContentLocked().waitForAllWindowsDrawn(); + container.waitForAllWindowsDrawn(); mWindowPlacerLocked.requestTraversal(); - mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT); - if (mWaitingForDrawn.isEmpty()) { + mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container); + if (container.mWaitingForDrawn.isEmpty()) { allWindowsDrawn = true; } else { - mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, timeout); + mWaitingForDrawnCallbacks.put(container, callback); + mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout); checkDrawnWindowsLocked(); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 82b9d460029d..56e08b2843b5 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3497,7 +3497,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override void setWaitingForDrawnIfResizingChanged() { if (isDragResizeChanged()) { - mWmService.mWaitingForDrawn.add(this); + mWmService.mRoot.mWaitingForDrawn.add(this); } super.setWaitingForDrawnIfResizingChanged(); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index b118cdfeb84d..47291cb286ba 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -11320,6 +11320,28 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + @Override + public boolean isActiveSupervisionApp(int uid) { + synchronized (getLockObject()) { + final ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked( + null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, uid); + if (admin == null) { + return false; + } + + final String supervisionString = mContext.getResources().getString( + com.android.internal.R.string + .config_defaultSupervisionProfileOwnerComponent); + if (supervisionString == null) { + return false; + } + + final ComponentName supervisorComponent = ComponentName.unflattenFromString( + supervisionString); + return admin.info.getComponent().equals(supervisorComponent); + } + } + private void notifyCrossProfileProvidersChanged(int userId, List<String> packages) { final List<OnCrossProfileWidgetProvidersChangeListener> listeners; synchronized (getLockObject()) { diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index a25e40f8cc13..9a1fd9cf0e12 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -2650,6 +2650,21 @@ public class DevicePolicyManagerTest extends DpmTestBase { verifyStayOnWhilePluggedCleared(false); } + public void testIsActiveSupervisionApp() throws Exception { + when(mServiceContext.resources + .getString(R.string.config_defaultSupervisionProfileOwnerComponent)) + .thenReturn(admin1.flattenToString()); + + final int PROFILE_USER = 15; + final int PROFILE_ADMIN = UserHandle.getUid(PROFILE_USER, 19436); + addManagedProfile(admin1, PROFILE_ADMIN, admin1); + mContext.binder.callingUid = PROFILE_ADMIN; + + final DevicePolicyManagerInternal dpmi = + LocalServices.getService(DevicePolicyManagerInternal.class); + assertTrue(dpmi.isActiveSupervisionApp(PROFILE_ADMIN)); + } + // Test if lock timeout on managed profile is handled correctly depending on whether profile // uses separate challenge. public void testSetMaximumTimeToLockProfile() throws Exception { diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java index 1a3dde0cb838..2133a7d3550b 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java @@ -24,6 +24,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.Arrays; +import java.util.Collections; + @RunWith(JUnit4.class) public class OpenFormulaTest { @@ -34,12 +37,11 @@ public class OpenFormulaTest { @Test public void testValidOpenFormula() { - OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND, ATOMIC_FORMULA_1, - ATOMIC_FORMULA_2); + OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND, + Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)); assertEquals(OpenFormula.Connector.AND, openFormula.getConnector()); - assertEquals(ATOMIC_FORMULA_1, openFormula.getMainFormula()); - assertEquals(ATOMIC_FORMULA_2, openFormula.getAuxiliaryFormula()); + assertEquals(Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2), openFormula.getFormulas()); } @Test @@ -47,9 +49,10 @@ public class OpenFormulaTest { assertExpectException( IllegalArgumentException.class, /* expectedExceptionMessageRegex */ - String.format("Invalid formulas used for connector %s", OpenFormula.Connector.AND), - () -> new OpenFormula(OpenFormula.Connector.AND, ATOMIC_FORMULA_1, - null)); + String.format("Connector %s must have at least 2 formulas", + OpenFormula.Connector.AND), + () -> new OpenFormula(OpenFormula.Connector.AND, + Collections.singletonList(ATOMIC_FORMULA_1))); } @Test @@ -57,8 +60,8 @@ public class OpenFormulaTest { assertExpectException( IllegalArgumentException.class, /* expectedExceptionMessageRegex */ - String.format("Invalid formulas used for connector %s", OpenFormula.Connector.NOT), - () -> new OpenFormula(OpenFormula.Connector.NOT, ATOMIC_FORMULA_1, - ATOMIC_FORMULA_2)); + String.format("Connector %s must have 1 formula only", OpenFormula.Connector.NOT), + () -> new OpenFormula(OpenFormula.Connector.NOT, + Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2))); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index 7115af9f1ee9..b9fef4b008c3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -32,7 +32,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.any; import android.content.ComponentName; -import android.content.pm.PackageList; import android.content.pm.PackageManagerInternal; import android.graphics.Rect; import android.os.UserHandle; @@ -43,6 +42,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import com.android.server.LocalServices; +import com.android.server.pm.PackageList; import com.android.server.wm.LaunchParamsController.LaunchParams; import org.junit.Before; diff --git a/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java index 778f0ca3c782..9c3ff654ddf0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java @@ -22,8 +22,11 @@ import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static org.mockito.ArgumentMatchers.any; + import android.hardware.display.DisplayManagerGlobal; import android.view.Display; import android.view.DisplayInfo; @@ -85,6 +88,10 @@ class TestActivityDisplay extends ActivityDisplay { displayRotation.setRotation(rotation); return true; }).when(displayRotation).updateRotationUnchecked(anyBoolean()); + + final InputMonitor inputMonitor = mDisplayContent.getInputMonitor(); + spyOn(inputMonitor); + doNothing().when(inputMonitor).resumeDispatchingLw(any()); } @SuppressWarnings("TypeParameterUnusedInFormals") diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 34eb3f16bf2a..ecee709054ee 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -1733,10 +1733,13 @@ public class UsageStatsService extends SystemService implements public void registerAppUsageLimitObserver(int observerId, String[] packages, long timeLimitMs, long timeUsedMs, PendingIntent callbackIntent, String callingPackage) { + final int callingUid = Binder.getCallingUid(); + final DevicePolicyManagerInternal dpmInternal = getDpmInternal(); if (!hasPermissions(callingPackage, - Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)) { - throw new SecurityException("Caller doesn't have both SUSPEND_APPS and " - + "OBSERVE_APP_USAGE permissions"); + Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE) + && (dpmInternal != null && !dpmInternal.isActiveSupervisionApp(callingUid))) { + throw new SecurityException("Caller must be the active supervision app or " + + "it must have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions"); } if (packages == null || packages.length == 0) { @@ -1745,7 +1748,6 @@ public class UsageStatsService extends SystemService implements if (callbackIntent == null && timeUsedMs < timeLimitMs) { throw new NullPointerException("callbackIntent can't be null"); } - final int callingUid = Binder.getCallingUid(); final int userId = UserHandle.getUserId(callingUid); final long token = Binder.clearCallingIdentity(); try { @@ -1758,13 +1760,15 @@ public class UsageStatsService extends SystemService implements @Override public void unregisterAppUsageLimitObserver(int observerId, String callingPackage) { + final int callingUid = Binder.getCallingUid(); + final DevicePolicyManagerInternal dpmInternal = getDpmInternal(); if (!hasPermissions(callingPackage, - Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)) { - throw new SecurityException("Caller doesn't have both SUSPEND_APPS and " - + "OBSERVE_APP_USAGE permissions"); + Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE) + && (dpmInternal != null && !dpmInternal.isActiveSupervisionApp(callingUid))) { + throw new SecurityException("Caller must be the active supervision app or " + + "it must have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions"); } - final int callingUid = Binder.getCallingUid(); final int userId = UserHandle.getUserId(callingUid); final long token = Binder.clearCallingIdentity(); try { diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java index 28747dab38db..9ff851598648 100644 --- a/telephony/java/android/telephony/TelephonyScanManager.java +++ b/telephony/java/android/telephony/TelephonyScanManager.java @@ -104,7 +104,7 @@ public final class TelephonyScanManager { private final Looper mLooper; private final Messenger mMessenger; - private SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>(); + private final SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>(); public TelephonyScanManager() { HandlerThread thread = new HandlerThread(TAG); @@ -204,14 +204,16 @@ public final class TelephonyScanManager { try { ITelephony telephony = getITelephony(); if (telephony != null) { - int scanId = telephony.requestNetworkScan( - subId, request, mMessenger, new Binder(), callingPackage); - if (scanId == INVALID_SCAN_ID) { - Rlog.e(TAG, "Failed to initiate network scan"); - return null; + synchronized (mScanInfo) { + int scanId = telephony.requestNetworkScan( + subId, request, mMessenger, new Binder(), callingPackage); + if (scanId == INVALID_SCAN_ID) { + Rlog.e(TAG, "Failed to initiate network scan"); + return null; + } + saveScanInfo(scanId, request, executor, callback); + return new NetworkScan(scanId, subId); } - saveScanInfo(scanId, request, executor, callback); - return new NetworkScan(scanId, subId); } } catch (RemoteException ex) { Rlog.e(TAG, "requestNetworkScan RemoteException", ex); @@ -223,9 +225,7 @@ public final class TelephonyScanManager { private void saveScanInfo( int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) { - synchronized (mScanInfo) { - mScanInfo.put(id, new NetworkScanInfo(request, executor, callback)); - } + mScanInfo.put(id, new NetworkScanInfo(request, executor, callback)); } private ITelephony getITelephony() { diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java index c9ec0f86a9dc..c19ae7b3916a 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -170,7 +170,7 @@ public class PhoneConstants { public static final int RIL_CARD_MAX_APPS = 8; - public static final int DEFAULT_CARD_INDEX = 0; + public static final int DEFAULT_SLOT_INDEX = 0; public static final int MAX_PHONE_COUNT_SINGLE_SIM = 1; diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java index b0464d9e656f..ae8285b8a908 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/tests/net/common/java/android/net/LinkPropertiesTest.java @@ -99,6 +99,7 @@ public class LinkPropertiesTest { assertFalse(lp.isIpv4Provisioned()); assertFalse(lp.isIpv6Provisioned()); assertFalse(lp.isPrivateDnsActive()); + assertFalse(lp.isWakeOnLanSupported()); } private LinkProperties makeTestObject() { @@ -120,6 +121,7 @@ public class LinkPropertiesTest { lp.setMtu(MTU); lp.setTcpBufferSizes(TCP_BUFFER_SIZES); lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96")); + lp.setWakeOnLanSupported(true); return lp; } @@ -158,6 +160,9 @@ public class LinkPropertiesTest { assertTrue(source.isIdenticalTcpBufferSizes(target)); assertTrue(target.isIdenticalTcpBufferSizes(source)); + assertTrue(source.isIdenticalWakeOnLan(target)); + assertTrue(target.isIdenticalWakeOnLan(source)); + // Check result of equals(). assertTrue(source.equals(target)); assertTrue(target.equals(source)); @@ -1057,4 +1062,13 @@ public class LinkPropertiesTest { lp.clear(); assertFalse(lp.isPrivateDnsActive()); } + + @Test + public void testWakeOnLanSupported() { + final LinkProperties lp = makeTestObject(); + assertTrue(lp.isWakeOnLanSupported()); + + lp.clear(); + assertFalse(lp.isWakeOnLanSupported()); + } } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 41440abadcd0..bffbbfda08ee 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -274,6 +274,7 @@ public class ConnectivityServiceTest { private static final String CLAT_PREFIX = "v4-"; private static final String MOBILE_IFNAME = "test_rmnet_data0"; private static final String WIFI_IFNAME = "test_wlan0"; + private static final String WIFI_WOL_IFNAME = "test_wlan_wol"; private static final String[] EMPTY_STRING_ARRAY = new String[0]; private MockContext mServiceContext; @@ -343,6 +344,12 @@ public class ConnectivityServiceTest { "mobile_mms,2,0,2,60000,true", }); + when(mResources.getStringArray( + com.android.internal.R.array.config_wakeonlan_supported_interfaces)) + .thenReturn(new String[]{ + WIFI_WOL_IFNAME, + }); + mContentResolver = new MockContentResolver(); mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider); } @@ -5947,6 +5954,24 @@ public class ConnectivityServiceTest { assertContainsExactly(uidCaptor.getValue(), APP2_UID); } + @Test + public void testLinkPropertiesWithWakeOnLanForActiveNetwork() throws Exception { + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + + LinkProperties wifiLp = new LinkProperties(); + wifiLp.setInterfaceName(WIFI_WOL_IFNAME); + wifiLp.setWakeOnLanSupported(false); + + // Default network switch should update ifaces. + mWiFiNetworkAgent.connect(false); + mWiFiNetworkAgent.sendLinkProperties(wifiLp); + waitForIdle(); + + // ConnectivityService should have changed the WakeOnLanSupported to true + wifiLp.setWakeOnLanSupported(true); + assertEquals(wifiLp, mService.getActiveLinkProperties()); + } + private TestNetworkAgentWrapper establishVpn(LinkProperties lp, int establishingUid, Set<UidRange> vpnRange) throws Exception { diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java index 702921836b0d..3fdba6eac55d 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -54,7 +54,6 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; -import android.content.pm.PackageList; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; @@ -70,6 +69,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; +import com.android.server.pm.PackageList; import org.junit.Before; import org.junit.Test; |