diff options
161 files changed, 3283 insertions, 1126 deletions
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 2834ab14f28d..94e5d0b2591b 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -1098,8 +1098,7 @@ public class AppStandbyController implements AppStandbyInternal { if (mAppWidgetManager != null && mInjector.isBoundWidgetPackage(mAppWidgetManager, packageName, userId)) { - // TODO: consider lowering to ACTIVE - return STANDBY_BUCKET_EXEMPTED; + return STANDBY_BUCKET_ACTIVE; } if (isDeviceProvisioningPackage(packageName)) { diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index 5cf5e0b1d182..cbc8ed636ff2 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -662,14 +662,19 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { return; } + // Cleann up from previous statsd - cancel any alarms that had been set. Do this here + // instead of in binder death because statsd can come back and set different alarms, or not + // want to set an alarm when it had been set. This guarantees that when we get a new statsd, + // we cancel any alarms before it is able to set them. + cancelAnomalyAlarm(); + cancelPullingAlarm(); + cancelAlarmForSubscriberTriggering(); + if (DEBUG) Log.d(TAG, "Saying hi to statsd"); mStatsManagerService.statsdReady(statsd); try { statsd.statsCompanionReady(); - cancelAnomalyAlarm(); - cancelPullingAlarm(); - BroadcastReceiver appUpdateReceiver = new AppUpdateReceiver(); BroadcastReceiver userUpdateReceiver = new UserUpdateReceiver(); BroadcastReceiver shutdownEventReceiver = new ShutdownEventReceiver(); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index f00a35d10819..8ba52ef06c44 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -440,6 +440,7 @@ message Atom { 273 [(module) = "permissioncontroller"]; EvsUsageStatsReported evs_usage_stats_reported = 274 [(module) = "evs"]; AudioPowerUsageDataReported audio_power_usage_data_reported = 275; + TvTunerStateChanged tv_tuner_state_changed = 276 [(module) = "framework"]; SdkExtensionStatus sdk_extension_status = 354; // StatsdStats tracks platform atoms with ids upto 500. @@ -447,7 +448,7 @@ message Atom { } // Pulled events will start at field 10000. - // Next: 10082 + // Next: 10084 oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"]; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"]; @@ -539,6 +540,9 @@ message Atom { SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"]; SettingSnapshot setting_snapshot = 10080 [(module) = "framework"]; DisplayWakeReason display_wake_reason = 10081 [(module) = "framework"]; + DataUsageBytesTransfer data_usage_bytes_transfer = 10082 [(module) = "framework"]; + BytesTransferByTagAndMetered bytes_transfer_by_tag_and_metered = + 10083 [(module) = "framework"]; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -4940,6 +4944,52 @@ message MobileBytesTransferByFgBg { } /** + * Used for pull network statistics via mobile|wifi networks, and sliced by interesting dimensions. + * Note that data is expected to be sliced into more dimensions in future. In other words, + * the caller must not assume the data is unique when filtering with a set of matching conditions. + * Thus, as the dimension grows, the caller will not be affected. + * + * Pulled from: + * StatsPullAtomService (using NetworkStatsService to get NetworkStats) + */ +message DataUsageBytesTransfer { + // State of this record. Should be NetworkStats#SET_DEFAULT or NetworkStats#SET_FOREGROUND to + // indicate the foreground state, or NetworkStats#SET_ALL to indicate the record is for all + // states combined, not including debug states. See NetworkStats#SET_*. + optional int32 state = 1; + + optional int64 rx_bytes = 2; + + optional int64 rx_packets = 3; + + optional int64 tx_bytes = 4; + + optional int64 tx_packets = 5; + + // Radio Access Technology (RAT) type of this record, should be one of + // TelephonyManager#NETWORK_TYPE_* constants, or NetworkTemplate#NETWORK_TYPE_ALL to indicate + // the record is for all rat types combined. + optional int32 rat_type = 6; + + // Mcc/Mnc read from sim if the record is for a specific subscription, null indicates the + // record is combined regardless of subscription. + optional string sim_mcc = 7; + optional string sim_mnc = 8; + + // Enumeration of opportunistic states with an additional ALL state indicates the record is + // combined regardless of the boolean value in its field. + enum DataSubscriptionState { + ALL = 1; + OPPORTUNISTIC = 2; + NOT_OPPORTUNISTIC = 3; + } + // Mark whether the subscription is an opportunistic data subscription, and ALL indicates the + // record is combined regardless of opportunistic data subscription. + // See {@link SubscriptionManager#setOpportunistic}. + optional DataSubscriptionState opportunistic_data_sub = 9; +} + +/** * Pulls bytes transferred via bluetooth. It is pulled from Bluetooth controller. * * Pulled from: @@ -5982,6 +6032,12 @@ message PackageNotificationChannelPreferences { optional int32 user_locked_fields = 6; // Indicates if the channel was deleted by the app. optional bool is_deleted = 7; + // Indicates if the channel was marked as a conversation by the app. + optional bool is_conversation = 8; + // Indicates if the channel is a conversation that was demoted by the user. + optional bool is_demoted_conversation = 9; + // Indicates if the channel is a conversation that was marked as important by the user. + optional bool is_important_conversation = 10; } /** @@ -9121,6 +9177,28 @@ message SdkExtensionStatus { } /** + * Logs when a tune occurs through device's Frontend. + * This is atom ID 276. + * + * Logged from: + * frameworks/base/media/java/android/media/tv/tuner/Tuner.java + */ +message TvTunerStateChanged { + enum State { + UNKNOWN = 0; + TUNING = 1; // Signal is tuned + LOCKED = 2; // the signal is locked + NOT_LOCKED = 3; // the signal isn’t locked. + SIGNAL_LOST = 4; // the signal was locked, but is lost now. + SCANNING = 5; // the signal is scanned + SCAN_STOPPED = 6; // the scan is stopped. + } + // The uid of the application that sent this custom atom. + optional int32 uid = 1 [(is_uid) = true]; + // new state + optional State state = 2; +} +/** * Logs when an app is frozen or unfrozen. * * Logged from: @@ -9756,3 +9834,25 @@ message AudioPowerUsageDataReported { } optional AudioType type = 4; } + +/** + * Pulls bytes transferred over WiFi and mobile networks sliced by uid, is_metered, and tag. + * + * Pulled from: + * StatsPullAtomService, which uses NetworkStatsService to query NetworkStats. + */ +message BytesTransferByTagAndMetered { + optional int32 uid = 1 [(is_uid) = true]; + + optional bool is_metered = 2; + + optional int32 tag = 3; + + optional int64 rx_bytes = 4; + + optional int64 rx_packets = 5; + + optional int64 tx_bytes = 6; + + optional int64 tx_packets = 7; +} diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 21ffff32f539..d865c2176c1e 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -122,11 +122,11 @@ CountMetricProducer::~CountMetricProducer() { } void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, int oldState, - int newState) { + const HashableDimensionKey& primaryKey, + const FieldValue& oldState, const FieldValue& newState) { VLOG("CountMetric %lld onStateChanged time %lld, State%d, key %s, %d -> %d", (long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(), - oldState, newState); + oldState.mValue.int_value, newState.mValue.int_value); } void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index f9a8842efc3d..26b3d3cc6722 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -53,8 +53,8 @@ public: virtual ~CountMetricProducer(); void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, int oldState, - int newState) override; + const HashableDimensionKey& primaryKey, const FieldValue& oldState, + const FieldValue& newState) override; protected: void onMatchedLogEventInternalLocked( diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 0de92f3d9f47..663365924829 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -161,13 +161,12 @@ sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker( void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId, const HashableDimensionKey& primaryKey, - const int32_t oldState, const int32_t newState) { - // Create a FieldValue object to hold the new state. - FieldValue value; - value.mValue.setInt(newState); + const FieldValue& oldState, + const FieldValue& newState) { // Check if this metric has a StateMap. If so, map the new state value to // the correct state group id. - mapStateValue(atomId, &value); + FieldValue newStateCopy = newState; + mapStateValue(atomId, &newStateCopy); flushIfNeededLocked(eventTimeNs); @@ -185,7 +184,7 @@ void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int if (!containsLinkedStateValues(whatIt.first, primaryKey, mMetric2StateLinks, atomId)) { continue; } - whatIt.second->onStateChanged(eventTimeNs, atomId, value); + whatIt.second->onStateChanged(eventTimeNs, atomId, newStateCopy); } } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 6f84076ee6b5..53f0f28c3386 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -55,8 +55,8 @@ public: const sp<AlarmMonitor>& anomalyAlarmMonitor) override; void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, const int32_t oldState, - const int32_t newState) override; + const HashableDimensionKey& primaryKey, const FieldValue& oldState, + const FieldValue& newState) override; protected: void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override; diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 28563ad4b0f5..5fabb5fb6ffc 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -182,8 +182,8 @@ public: }; void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, const int32_t oldState, - const int32_t newState){}; + const HashableDimensionKey& primaryKey, const FieldValue& oldState, + const FieldValue& newState){}; // Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp. // This method clears all the past buckets. diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index bf636a4f048d..f03ce4550bc4 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -182,15 +182,26 @@ ValueMetricProducer::~ValueMetricProducer() { } void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId, - const HashableDimensionKey& primaryKey, int oldState, - int newState) { + const HashableDimensionKey& primaryKey, + const FieldValue& oldState, const FieldValue& newState) { VLOG("ValueMetric %lld onStateChanged time %lld, State %d, key %s, %d -> %d", (long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(), - oldState, newState); + oldState.mValue.int_value, newState.mValue.int_value); // If condition is not true, we do not need to pull for this state change. if (mCondition != ConditionState::kTrue) { return; } + + // If old and new states are in the same StateGroup, then we do not need to + // pull for this state change. + FieldValue oldStateCopy = oldState; + FieldValue newStateCopy = newState; + mapStateValue(atomId, &oldStateCopy); + mapStateValue(atomId, &newStateCopy); + if (oldStateCopy == newStateCopy) { + return; + } + bool isEventLate = eventTimeNs < mCurrentBucketStartTimeNs; if (isEventLate) { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index c8dc8cc290c4..751fef2bf2b1 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -90,7 +90,7 @@ public: }; void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey, - int oldState, int newState) override; + const FieldValue& oldState, const FieldValue& newState) override; protected: void onMatchedLogEventInternalLocked( diff --git a/cmds/statsd/src/state/StateListener.h b/cmds/statsd/src/state/StateListener.h index d1af1968ac38..63880017ca18 100644 --- a/cmds/statsd/src/state/StateListener.h +++ b/cmds/statsd/src/state/StateListener.h @@ -45,8 +45,8 @@ public: * [newState]: Current state value after state change */ virtual void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, int oldState, - int newState) = 0; + const HashableDimensionKey& primaryKey, const FieldValue& oldState, + const FieldValue& newState) = 0; }; } // namespace statsd diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp index b63713b64c5d..41e525c343ba 100644 --- a/cmds/statsd/src/state/StateTracker.cpp +++ b/cmds/statsd/src/state/StateTracker.cpp @@ -35,31 +35,30 @@ void StateTracker::onLogEvent(const LogEvent& event) { HashableDimensionKey primaryKey; filterPrimaryKey(event.getValues(), &primaryKey); - FieldValue stateValue; - if (!getStateFieldValueFromLogEvent(event, &stateValue)) { + FieldValue newState; + if (!getStateFieldValueFromLogEvent(event, &newState)) { ALOGE("StateTracker error extracting state from log event. Missing exclusive state field."); clearStateForPrimaryKey(eventTimeNs, primaryKey); return; } - mField.setField(stateValue.mField.getField()); + mField.setField(newState.mField.getField()); - if (stateValue.mValue.getType() != INT) { + if (newState.mValue.getType() != INT) { ALOGE("StateTracker error extracting state from log event. Type: %d", - stateValue.mValue.getType()); + newState.mValue.getType()); clearStateForPrimaryKey(eventTimeNs, primaryKey); return; } - const int32_t resetState = event.getResetState(); - if (resetState != -1) { + if (int resetState = event.getResetState(); resetState != -1) { VLOG("StateTracker new reset state: %d", resetState); - handleReset(eventTimeNs, resetState); + const FieldValue resetStateFieldValue(mField, Value(resetState)); + handleReset(eventTimeNs, resetStateFieldValue); return; } - const int32_t newState = stateValue.mValue.int_value; - const bool nested = stateValue.mAnnotations.isNested(); + const bool nested = newState.mAnnotations.isNested(); StateValueInfo* stateValueInfo = &mStateMap[primaryKey]; updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo); } @@ -85,7 +84,7 @@ bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValu return false; } -void StateTracker::handleReset(const int64_t eventTimeNs, const int32_t newState) { +void StateTracker::handleReset(const int64_t eventTimeNs, const FieldValue& newState) { VLOG("StateTracker handle reset"); for (auto& [primaryKey, stateValueInfo] : mStateMap) { updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, @@ -102,8 +101,9 @@ void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs, // If there is no entry for the primaryKey in mStateMap, then the state is already // kStateUnknown. + const FieldValue state(mField, Value(kStateUnknown)); if (it != mStateMap.end()) { - updateStateForPrimaryKey(eventTimeNs, primaryKey, kStateUnknown, + updateStateForPrimaryKey(eventTimeNs, primaryKey, state, false /* nested; treat this state change as not nested */, &it->second); } @@ -111,22 +111,26 @@ void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs, void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey, - const int32_t newState, const bool nested, + const FieldValue& newState, const bool nested, StateValueInfo* stateValueInfo) { - const int32_t oldState = stateValueInfo->state; + FieldValue oldState; + oldState.mField = mField; + oldState.mValue.setInt(stateValueInfo->state); + const int32_t oldStateValue = stateValueInfo->state; + const int32_t newStateValue = newState.mValue.int_value; - if (kStateUnknown == newState) { + if (kStateUnknown == newStateValue) { mStateMap.erase(primaryKey); } // Update state map for non-nested counting case. // Every state event triggers a state overwrite. if (!nested) { - stateValueInfo->state = newState; + stateValueInfo->state = newStateValue; stateValueInfo->count = 1; // Notify listeners if state has changed. - if (oldState != newState) { + if (oldStateValue != newStateValue) { notifyListeners(eventTimeNs, primaryKey, oldState, newState); } return; @@ -142,26 +146,26 @@ void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs, // In atoms.proto, a state atom with nested counting enabled // must only have 2 states. There is no enforcemnt here of this requirement. // The atom must be logged correctly. - if (kStateUnknown == newState) { - if (kStateUnknown != oldState) { + if (kStateUnknown == newStateValue) { + if (kStateUnknown != oldStateValue) { notifyListeners(eventTimeNs, primaryKey, oldState, newState); } - } else if (oldState == kStateUnknown) { - stateValueInfo->state = newState; + } else if (oldStateValue == kStateUnknown) { + stateValueInfo->state = newStateValue; stateValueInfo->count = 1; notifyListeners(eventTimeNs, primaryKey, oldState, newState); - } else if (oldState == newState) { + } else if (oldStateValue == newStateValue) { stateValueInfo->count++; } else if (--stateValueInfo->count == 0) { - stateValueInfo->state = newState; + stateValueInfo->state = newStateValue; stateValueInfo->count = 1; notifyListeners(eventTimeNs, primaryKey, oldState, newState); } } void StateTracker::notifyListeners(const int64_t eventTimeNs, - const HashableDimensionKey& primaryKey, const int32_t oldState, - const int32_t newState) { + const HashableDimensionKey& primaryKey, + const FieldValue& oldState, const FieldValue& newState) { for (auto l : mListeners) { auto sl = l.promote(); if (sl != nullptr) { diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h index c5f6315fc992..abd579e7e302 100644 --- a/cmds/statsd/src/state/StateTracker.h +++ b/cmds/statsd/src/state/StateTracker.h @@ -72,19 +72,19 @@ private: std::set<wp<StateListener>> mListeners; // Reset all state values in map to the given state. - void handleReset(const int64_t eventTimeNs, const int32_t newState); + void handleReset(const int64_t eventTimeNs, const FieldValue& newState); // Clears the state value mapped to the given primary key by setting it to kStateUnknown. void clearStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey); // Update the StateMap based on the received state value. void updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey, - const int32_t newState, const bool nested, + const FieldValue& newState, const bool nested, StateValueInfo* stateValueInfo); // Notify registered state listeners of state change. void notifyListeners(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey, - const int32_t oldState, const int32_t newState); + const FieldValue& oldState, const FieldValue& newState); }; bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output); diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index b6e1075bcb72..474aa2234837 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -3898,14 +3898,12 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5)); return true; })) - // Screen state change to VR. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector<std::shared_ptr<LogEvent>>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 9)); - return true; - })) + // Screen state change to VR has no pull because it is in the same + // state group as ON. + + // Screen state change to ON has no pull because it is in the same + // state group as VR. + // Screen state change to OFF. .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data, bool) { @@ -3969,23 +3967,33 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { EXPECT_EQ(true, it->second[0].hasValue); EXPECT_EQ(2, it->second[0].value.long_value); - // Bucket status after screen state change ON->VR (also ON). + // Bucket status after screen state change ON->VR. + // Both ON and VR are in the same state group, so the base should not change. screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10, android::view::DisplayStateEnum::DISPLAY_STATE_VR); StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); EXPECT_EQ(true, itBase->second[0].hasBase); - EXPECT_EQ(9, itBase->second[0].base.long_value); - // Value for dimension, state key {{}, ON GROUP} - EXPECT_EQ(screenOnGroup.group_id(), - it->first.getStateValuesKey().getValues()[0].mValue.long_value); + EXPECT_EQ(5, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, kStateUnknown} EXPECT_EQ(true, it->second[0].hasValue); - EXPECT_EQ(4, it->second[0].value.long_value); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Bucket status after screen state change VR->ON. + // Both ON and VR are in the same state group, so the base should not change. + screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12, + android::view::DisplayStateEnum::DISPLAY_STATE_ON); + StateManager::getInstance().onLogEvent(*screenEvent); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(5, itBase->second[0].base.long_value); // Value for dimension, state key {{}, kStateUnknown} - it++; EXPECT_EQ(true, it->second[0].hasValue); EXPECT_EQ(2, it->second[0].value.long_value); diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp index 13e8f5c343f2..530ac5e01f3e 100644 --- a/cmds/statsd/tests/state/StateTracker_test.cpp +++ b/cmds/statsd/tests/state/StateTracker_test.cpp @@ -50,8 +50,9 @@ public: std::vector<Update> updates; void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, int oldState, int newState) { - updates.emplace_back(primaryKey, newState); + const HashableDimensionKey& primaryKey, const FieldValue& oldState, + const FieldValue& newState) { + updates.emplace_back(primaryKey, newState.mValue.int_value); } }; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index eea1d69b6326..8e43ca3c6739 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1538,6 +1538,12 @@ public final class ActivityThread extends ClientTransactionHandler { IoUtils.closeQuietly(pfd); } + @Override + public void dumpCacheInfo(ParcelFileDescriptor pfd, String[] args) { + PropertyInvalidatedCache.dumpCacheInfo(pfd.getFileDescriptor(), args); + IoUtils.closeQuietly(pfd); + } + private File getDatabasesDir(Context context) { // There's no simple way to get the databases/ path, so do it this way. return context.getDatabasePath("a").getParentFile(); @@ -5902,6 +5908,12 @@ public final class ActivityThread extends ClientTransactionHandler { } } + /** + * Sets the supplied {@code overrideConfig} as pending for the {@code activityToken}. Calling + * this method prevents any calls to + * {@link #handleActivityConfigurationChanged(IBinder, Configuration, int, boolean)} from + * processing any configurations older than {@code overrideConfig}. + */ @Override public void updatePendingActivityConfiguration(IBinder activityToken, Configuration overrideConfig) { @@ -5918,13 +5930,22 @@ public final class ActivityThread extends ClientTransactionHandler { } synchronized (r) { + if (r.mPendingOverrideConfig != null + && !r.mPendingOverrideConfig.isOtherSeqNewer(overrideConfig)) { + if (DEBUG_CONFIGURATION) { + Slog.v(TAG, "Activity has newer configuration pending so drop this" + + " transaction. overrideConfig=" + overrideConfig + + " r.mPendingOverrideConfig=" + r.mPendingOverrideConfig); + } + return; + } r.mPendingOverrideConfig = overrideConfig; } } @Override public void handleActivityConfigurationChanged(IBinder activityToken, - Configuration overrideConfig, int displayId) { + @NonNull Configuration overrideConfig, int displayId) { handleActivityConfigurationChanged(activityToken, overrideConfig, displayId, // This is the only place that uses alwaysReportChange=true. The entry point should // be from ActivityConfigurationChangeItem or MoveToDisplayItem, so the server side @@ -5935,15 +5956,18 @@ public final class ActivityThread extends ClientTransactionHandler { } /** - * Handle new activity configuration and/or move to a different display. + * Handle new activity configuration and/or move to a different display. This method is a noop + * if {@link #updatePendingActivityConfiguration(IBinder, Configuration)} has been called with + * a newer config than {@code overrideConfig}. + * * @param activityToken Target activity token. * @param overrideConfig Activity override config. * @param displayId Id of the display where activity was moved to, -1 if there was no move and * value didn't change. * @param alwaysReportChange If the configuration is changed, always report to activity. */ - void handleActivityConfigurationChanged(IBinder activityToken, Configuration overrideConfig, - int displayId, boolean alwaysReportChange) { + void handleActivityConfigurationChanged(IBinder activityToken, + @NonNull Configuration overrideConfig, int displayId, boolean alwaysReportChange) { ActivityClientRecord r = mActivities.get(activityToken); // Check input params. if (r == null || r.activity == null) { @@ -5954,9 +5978,13 @@ public final class ActivityThread extends ClientTransactionHandler { && displayId != r.activity.getDisplayId(); synchronized (r) { - if (r.mPendingOverrideConfig != null - && !r.mPendingOverrideConfig.isOtherSeqNewer(overrideConfig)) { - overrideConfig = r.mPendingOverrideConfig; + if (overrideConfig.isOtherSeqNewer(r.mPendingOverrideConfig)) { + if (DEBUG_CONFIGURATION) { + Slog.v(TAG, "Activity has newer configuration pending so drop this" + + " transaction. overrideConfig=" + overrideConfig + + " r.mPendingOverrideConfig=" + r.mPendingOverrideConfig); + } + return; } r.mPendingOverrideConfig = null; } @@ -6202,6 +6230,12 @@ public final class ActivityThread extends ClientTransactionHandler { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "trimMemory"); if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Trimming memory to level: " + level); + if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { + for (PropertyInvalidatedCache pic : PropertyInvalidatedCache.getActiveCaches()) { + pic.clear(); + } + } + ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null); final int N = callbacks.size(); diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 3ce768944e48..be1681bc7cc6 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -229,7 +229,16 @@ interface IActivityTaskManager { void unregisterTaskStackListener(in ITaskStackListener listener); void setTaskResizeable(int taskId, int resizeableMode); void toggleFreeformWindowingMode(in IBinder token); - void resizeTask(int taskId, in Rect bounds, int resizeMode); + + /** + * Resize the task with given bounds + * + * @param taskId The id of the task to set the bounds for. + * @param bounds The new bounds. + * @param resizeMode Resize mode defined as {@code ActivityTaskManager#RESIZE_MODE_*} constants. + * @return Return true on success. Otherwise false. + */ + boolean resizeTask(int taskId, in Rect bounds, int resizeMode); void moveStackToDisplay(int stackId, int displayId); void removeStack(int stackId); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 1f6e4cac199a..6e9157e2a8c3 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -119,6 +119,7 @@ oneway interface IApplicationThread { boolean dumpInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable, in String[] args); void dumpGfxInfo(in ParcelFileDescriptor fd, in String[] args); + void dumpCacheInfo(in ParcelFileDescriptor fd, in String[] args); void dumpProvider(in ParcelFileDescriptor fd, IBinder servicetoken, in String[] args); void dumpDbInfo(in ParcelFileDescriptor fd, in String[] args); diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index 3110e18985b0..01cf2b94a842 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -26,12 +26,20 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FastPrintWriter; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.Random; +import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicLong; /** @@ -197,6 +205,14 @@ public abstract class PropertyInvalidatedCache<Query, Result> { @GuardedBy("sCorkLock") private static final HashMap<String, Integer> sCorks = new HashMap<>(); + /** + * Weakly references all cache objects in the current process, allowing us to iterate over + * them all for purposes like issuing debug dumps and reacting to memory pressure. + */ + @GuardedBy("sCorkLock") + private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches = + new WeakHashMap<>(); + private final Object mLock = new Object(); /** @@ -225,6 +241,11 @@ public abstract class PropertyInvalidatedCache<Query, Result> { private boolean mDisabled = false; /** + * Maximum number of entries the cache will maintain. + */ + private final int mMaxEntries; + + /** * Make a new property invalidated cache. * * @param maxEntries Maximum number of entries to cache; LRU discard @@ -232,6 +253,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { */ public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) { mPropertyName = propertyName; + mMaxEntries = maxEntries; mCache = new LinkedHashMap<Query, Result>( 2 /* start small */, 0.75f /* default load factor */, @@ -241,6 +263,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> { return size() > maxEntries; } }; + synchronized (sCorkLock) { + sCaches.put(this, null); + } } /** @@ -248,6 +273,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> { */ public final void clear() { synchronized (mLock) { + if (DEBUG) { + Log.d(TAG, "clearing cache for " + mPropertyName); + } mCache.clear(); } } @@ -710,4 +738,87 @@ public abstract class PropertyInvalidatedCache<Query, Result> { Log.d(TAG, "disabling all caches in the process"); sEnabled = false; } + + /** + * Returns a list of caches alive at the current time. + */ + public static ArrayList<PropertyInvalidatedCache> getActiveCaches() { + synchronized (sCorkLock) { + return new ArrayList<PropertyInvalidatedCache>(sCaches.keySet()); + } + } + + /** + * Returns a list of the active corks in a process. + */ + public static ArrayList<Map.Entry<String, Integer>> getActiveCorks() { + synchronized (sCorkLock) { + return new ArrayList<Map.Entry<String, Integer>>(sCorks.entrySet()); + } + } + + private void dumpContents(PrintWriter pw, String[] args) { + synchronized (mLock) { + pw.println(String.format(" Cache Property Name: %s", cacheName())); + pw.println(String.format(" Last Observed Nonce: %d", mLastSeenNonce)); + pw.println(String.format(" Current Size: %d, Max Size: %d", + mCache.entrySet().size(), mMaxEntries)); + pw.println(String.format(" Enabled: %s", mDisabled ? "false" : "true")); + + Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet(); + if (cacheEntries.size() == 0) { + pw.println(""); + return; + } + + pw.println(""); + pw.println(" Contents:"); + for (Map.Entry<Query, Result> entry : cacheEntries) { + String key = Objects.toString(entry.getKey()); + String value = Objects.toString(entry.getValue()); + + pw.println(String.format(" Key: %s\n Value: %s\n", key, value)); + } + } + } + + /** + * Dumps contents of every cache in the process to the provided FileDescriptor. + */ + public static void dumpCacheInfo(FileDescriptor fd, String[] args) { + ArrayList<PropertyInvalidatedCache> activeCaches; + ArrayList<Map.Entry<String, Integer>> activeCorks; + + try ( + FileOutputStream fout = new FileOutputStream(fd); + PrintWriter pw = new FastPrintWriter(fout); + ) { + if (!sEnabled) { + pw.println(" Caching is disabled in this process."); + return; + } + + synchronized (sCorkLock) { + activeCaches = getActiveCaches(); + activeCorks = getActiveCorks(); + + if (activeCorks.size() > 0) { + pw.println(" Corking Status:"); + for (int i = 0; i < activeCorks.size(); i++) { + Map.Entry<String, Integer> entry = activeCorks.get(i); + pw.println(String.format(" Property Name: %s Count: %d", + entry.getKey(), entry.getValue())); + } + } + } + + for (int i = 0; i < activeCaches.size(); i++) { + PropertyInvalidatedCache currentCache = activeCaches.get(i); + currentCache.dumpContents(pw, args); + pw.flush(); + } + } catch (IOException e) { + Log.e(TAG, "Failed to dump PropertyInvalidatedCache instances"); + } + } } diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java index 0d4e16bbb0f3..8b52242a6b6c 100644 --- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java +++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java @@ -19,6 +19,7 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.view.Display.INVALID_DISPLAY; +import android.annotation.NonNull; import android.app.ClientTransactionHandler; import android.content.res.Configuration; import android.os.IBinder; @@ -37,6 +38,8 @@ public class ActivityConfigurationChangeItem extends ClientTransactionItem { @Override public void preExecute(android.app.ClientTransactionHandler client, IBinder token) { + // Notify the client of an upcoming change in the token configuration. This ensures that + // batches of config change items only process the newest configuration. client.updatePendingActivityConfiguration(token, mConfiguration); } @@ -55,7 +58,11 @@ public class ActivityConfigurationChangeItem extends ClientTransactionItem { private ActivityConfigurationChangeItem() {} /** Obtain an instance initialized with provided params. */ - public static ActivityConfigurationChangeItem obtain(Configuration config) { + public static ActivityConfigurationChangeItem obtain(@NonNull Configuration config) { + if (config == null) { + throw new IllegalArgumentException("Config must not be null."); + } + ActivityConfigurationChangeItem instance = ObjectPool.obtain(ActivityConfigurationChangeItem.class); if (instance == null) { @@ -68,7 +75,7 @@ public class ActivityConfigurationChangeItem extends ClientTransactionItem { @Override public void recycle() { - mConfiguration = null; + mConfiguration = Configuration.EMPTY; ObjectPool.recycle(this); } diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java index f6d3dbdf5596..9a457a3aad40 100644 --- a/core/java/android/app/servertransaction/MoveToDisplayItem.java +++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java @@ -18,6 +18,7 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; +import android.annotation.NonNull; import android.app.ClientTransactionHandler; import android.content.res.Configuration; import android.os.IBinder; @@ -36,6 +37,13 @@ public class MoveToDisplayItem extends ClientTransactionItem { private Configuration mConfiguration; @Override + public void preExecute(ClientTransactionHandler client, IBinder token) { + // Notify the client of an upcoming change in the token configuration. This ensures that + // batches of config change items only process the newest configuration. + client.updatePendingActivityConfiguration(token, mConfiguration); + } + + @Override public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityMovedToDisplay"); @@ -49,7 +57,12 @@ public class MoveToDisplayItem extends ClientTransactionItem { private MoveToDisplayItem() {} /** Obtain an instance initialized with provided params. */ - public static MoveToDisplayItem obtain(int targetDisplayId, Configuration configuration) { + public static MoveToDisplayItem obtain(int targetDisplayId, + @NonNull Configuration configuration) { + if (configuration == null) { + throw new IllegalArgumentException("Configuration must not be null"); + } + MoveToDisplayItem instance = ObjectPool.obtain(MoveToDisplayItem.class); if (instance == null) { instance = new MoveToDisplayItem(); @@ -63,7 +76,7 @@ public class MoveToDisplayItem extends ClientTransactionItem { @Override public void recycle() { mTargetDisplayId = 0; - mConfiguration = null; + mConfiguration = Configuration.EMPTY; ObjectPool.recycle(this); } diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 75ce0dcc1d1d..3fef92b203b6 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -625,7 +625,10 @@ public class AppWidgetHostView extends FrameLayout { } } defaultView = inflater.inflate(layoutId, this, false); - defaultView.setOnClickListener(this::onDefaultViewClicked); + if (!(defaultView instanceof AdapterView)) { + // AdapterView does not support onClickListener + defaultView.setOnClickListener(this::onDefaultViewClicked); + } } else { Log.w(TAG, "can't inflate defaultView because mInfo is missing"); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 9832bc1b79d2..2f488cdc3158 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -385,6 +385,9 @@ public abstract class PackageManager { * <p> * Note: this flag may cause less information about currently installed * applications to be returned. + * <p> + * Note: use of this flag requires the android.permission.QUERY_ALL_PACKAGES + * permission to see uninstalled packages. */ public static final int MATCH_UNINSTALLED_PACKAGES = 0x00002000; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 312e98e77636..d086459080f7 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -127,7 +127,6 @@ import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; /** * Parser for package files (APKs) on disk. This supports apps packaged either @@ -239,11 +238,6 @@ public class PackageParser { public static final boolean LOG_UNSAFE_BROADCASTS = false; - /** - * Total number of packages that were read from the cache. We use it only for logging. - */ - public static final AtomicInteger sCachedPackageReadCount = new AtomicInteger(); - // Set of broadcast actions that are safe for manifest receivers public static final Set<String> SAFE_BROADCASTS = new ArraySet<>(); static { diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java index 9b809b86eae9..b978ae559390 100644 --- a/core/java/android/database/DatabaseUtils.java +++ b/core/java/android/database/DatabaseUtils.java @@ -191,6 +191,58 @@ public class DatabaseUtils { } } + /** {@hide} */ + public static long executeInsert(@NonNull SQLiteDatabase db, @NonNull String sql, + @Nullable Object[] bindArgs) throws SQLException { + try (SQLiteStatement st = db.compileStatement(sql)) { + bindArgs(st, bindArgs); + return st.executeInsert(); + } + } + + /** {@hide} */ + public static int executeUpdateDelete(@NonNull SQLiteDatabase db, @NonNull String sql, + @Nullable Object[] bindArgs) throws SQLException { + try (SQLiteStatement st = db.compileStatement(sql)) { + bindArgs(st, bindArgs); + return st.executeUpdateDelete(); + } + } + + /** {@hide} */ + private static void bindArgs(@NonNull SQLiteStatement st, @Nullable Object[] bindArgs) { + if (bindArgs == null) return; + + for (int i = 0; i < bindArgs.length; i++) { + final Object bindArg = bindArgs[i]; + switch (getTypeOfObject(bindArg)) { + case Cursor.FIELD_TYPE_NULL: + st.bindNull(i + 1); + break; + case Cursor.FIELD_TYPE_INTEGER: + st.bindLong(i + 1, ((Number) bindArg).longValue()); + break; + case Cursor.FIELD_TYPE_FLOAT: + st.bindDouble(i + 1, ((Number) bindArg).doubleValue()); + break; + case Cursor.FIELD_TYPE_BLOB: + st.bindBlob(i + 1, (byte[]) bindArg); + break; + case Cursor.FIELD_TYPE_STRING: + default: + if (bindArg instanceof Boolean) { + // Provide compatibility with legacy + // applications which may pass Boolean values in + // bind args. + st.bindLong(i + 1, ((Boolean) bindArg).booleanValue() ? 1 : 0); + } else { + st.bindString(i + 1, bindArg.toString()); + } + break; + } + } + } + /** * Binds the given Object to the given SQLiteProgram using the proper * typing. For example, bind numbers as longs/doubles, and everything else diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index 36ec67ee1471..669d0466fdf2 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -626,7 +626,7 @@ public class SQLiteQueryBuilder { Log.d(TAG, sql); } } - return db.executeSql(sql, sqlArgs); + return DatabaseUtils.executeInsert(db, sql, sqlArgs); } /** @@ -702,7 +702,7 @@ public class SQLiteQueryBuilder { Log.d(TAG, sql); } } - return db.executeSql(sql, sqlArgs); + return DatabaseUtils.executeUpdateDelete(db, sql, sqlArgs); } /** @@ -762,7 +762,7 @@ public class SQLiteQueryBuilder { Log.d(TAG, sql); } } - return db.executeSql(sql, sqlArgs); + return DatabaseUtils.executeUpdateDelete(db, sql, sqlArgs); } private void enforceStrictColumns(@Nullable String[] projection) { diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index 4bed985489ef..f9ed2f851552 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -314,6 +314,92 @@ public class SoundTrigger { } @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ModuleProperties)) { + return false; + } + ModuleProperties other = (ModuleProperties) obj; + if (mId != other.mId) { + return false; + } + if (!mImplementor.equals(other.mImplementor)) { + return false; + } + if (!mDescription.equals(other.mDescription)) { + return false; + } + if (!mUuid.equals(other.mUuid)) { + return false; + } + if (mVersion != other.mVersion) { + return false; + } + if (!mSupportedModelArch.equals(other.mSupportedModelArch)) { + return false; + } + if (mMaxSoundModels != other.mMaxSoundModels) { + return false; + } + if (mMaxKeyphrases != other.mMaxKeyphrases) { + return false; + } + if (mMaxUsers != other.mMaxUsers) { + return false; + } + if (mRecognitionModes != other.mRecognitionModes) { + return false; + } + if (mSupportsCaptureTransition != other.mSupportsCaptureTransition) { + return false; + } + if (mMaxBufferMillis != other.mMaxBufferMillis) { + return false; + } + if (mSupportsConcurrentCapture != other.mSupportsConcurrentCapture) { + return false; + } + if (mPowerConsumptionMw != other.mPowerConsumptionMw) { + return false; + } + if (mReturnsTriggerInEvent != other.mReturnsTriggerInEvent) { + return false; + } + if (mAudioCapabilities != other.mAudioCapabilities) { + return false; + } + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + mId; + result = prime * result + mImplementor.hashCode(); + result = prime * result + mDescription.hashCode(); + result = prime * result + mUuid.hashCode(); + result = prime * result + mVersion; + result = prime * result + mSupportedModelArch.hashCode(); + result = prime * result + mMaxSoundModels; + result = prime * result + mMaxKeyphrases; + result = prime * result + mMaxUsers; + result = prime * result + mRecognitionModes; + result = prime * result + (mSupportsCaptureTransition ? 1 : 0); + result = prime * result + mMaxBufferMillis; + result = prime * result + (mSupportsConcurrentCapture ? 1 : 0); + result = prime * result + mPowerConsumptionMw; + result = prime * result + (mReturnsTriggerInEvent ? 1 : 0); + result = prime * result + mAudioCapabilities; + return result; + } + + @Override public String toString() { return "ModuleProperties [id=" + getId() + ", implementor=" + getImplementor() + ", description=" + getDescription() + ", uuid=" + getUuid() diff --git a/core/java/android/util/AtomicFile.java b/core/java/android/util/AtomicFile.java index cf7ed9b0566d..da7503d01428 100644 --- a/core/java/android/util/AtomicFile.java +++ b/core/java/android/util/AtomicFile.java @@ -29,31 +29,32 @@ import java.io.IOException; import java.util.function.Consumer; /** - * Helper class for performing atomic operations on a file by creating a - * backup file until a write has successfully completed. If you need this - * on older versions of the platform you can use - * {@link android.support.v4.util.AtomicFile} in the v4 support library. + * Helper class for performing atomic operations on a file by writing to a new file and renaming it + * into the place of the original file after the write has successfully completed. If you need this + * on older versions of the platform you can use {@link androidx.core.util.AtomicFile} in AndroidX. * <p> - * Atomic file guarantees file integrity by ensuring that a file has - * been completely written and sync'd to disk before removing its backup. - * As long as the backup file exists, the original file is considered - * to be invalid (left over from a previous attempt to write the file). - * </p><p> - * Atomic file does not confer any file locking semantics. - * Do not use this class when the file may be accessed or modified concurrently - * by multiple threads or processes. The caller is responsible for ensuring - * appropriate mutual exclusion invariants whenever it accesses the file. - * </p> + * Atomic file guarantees file integrity by ensuring that a file has been completely written and + * sync'd to disk before renaming it to the original file. Previously this is done by renaming the + * original file to a backup file beforehand, but this approach couldn't handle the case where the + * file is created for the first time. This class will also handle the backup file created by the + * old implementation properly. + * <p> + * Atomic file does not confer any file locking semantics. Do not use this class when the file may + * be accessed or modified concurrently by multiple threads or processes. The caller is responsible + * for ensuring appropriate mutual exclusion invariants whenever it accesses the file. */ public class AtomicFile { + private static final String LOG_TAG = "AtomicFile"; + private final File mBaseName; - private final File mBackupName; + private final File mNewName; + private final File mLegacyBackupName; private final String mCommitTag; private long mStartTime; /** * Create a new AtomicFile for a file located at the given File path. - * The secondary backup file will be the same file path with ".bak" appended. + * The new file created when writing will be the same file path with ".new" appended. */ public AtomicFile(File baseName) { this(baseName, null); @@ -65,7 +66,8 @@ public class AtomicFile { */ public AtomicFile(File baseName, String commitTag) { mBaseName = baseName; - mBackupName = new File(baseName.getPath() + ".bak"); + mNewName = new File(baseName.getPath() + ".new"); + mLegacyBackupName = new File(baseName.getPath() + ".bak"); mCommitTag = commitTag; } @@ -78,11 +80,12 @@ public class AtomicFile { } /** - * Delete the atomic file. This deletes both the base and backup files. + * Delete the atomic file. This deletes both the base and new files. */ public void delete() { mBaseName.delete(); - mBackupName.delete(); + mNewName.delete(); + mLegacyBackupName.delete(); } /** @@ -112,36 +115,28 @@ public class AtomicFile { public FileOutputStream startWrite(long startTime) throws IOException { mStartTime = startTime; - // Rename the current file so it may be used as a backup during the next read - if (mBaseName.exists()) { - if (!mBackupName.exists()) { - if (!mBaseName.renameTo(mBackupName)) { - Log.w("AtomicFile", "Couldn't rename file " + mBaseName - + " to backup file " + mBackupName); - } - } else { - mBaseName.delete(); + if (mLegacyBackupName.exists()) { + if (!mLegacyBackupName.renameTo(mBaseName)) { + Log.e(LOG_TAG, "Failed to rename legacy backup file " + mLegacyBackupName + + " to base file " + mBaseName); } } - FileOutputStream str = null; + try { - str = new FileOutputStream(mBaseName); + return new FileOutputStream(mNewName); } catch (FileNotFoundException e) { - File parent = mBaseName.getParentFile(); + File parent = mNewName.getParentFile(); if (!parent.mkdirs()) { - throw new IOException("Couldn't create directory " + mBaseName); + throw new IOException("Failed to create directory for " + mNewName); } - FileUtils.setPermissions( - parent.getPath(), - FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, - -1, -1); + FileUtils.setPermissions(parent.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG + | FileUtils.S_IXOTH, -1, -1); try { - str = new FileOutputStream(mBaseName); + return new FileOutputStream(mNewName); } catch (FileNotFoundException e2) { - throw new IOException("Couldn't create " + mBaseName); + throw new IOException("Failed to create new file " + mNewName, e2); } } - return str; } /** @@ -151,36 +146,45 @@ public class AtomicFile { * will return the new file stream. */ public void finishWrite(FileOutputStream str) { - if (str != null) { - FileUtils.sync(str); - try { - str.close(); - mBackupName.delete(); - } catch (IOException e) { - Log.w("AtomicFile", "finishWrite: Got exception:", e); - } - if (mCommitTag != null) { - com.android.internal.logging.EventLogTags.writeCommitSysConfigFile( - mCommitTag, SystemClock.uptimeMillis() - mStartTime); - } + if (str == null) { + return; + } + if (!FileUtils.sync(str)) { + Log.e(LOG_TAG, "Failed to sync file output stream"); + } + try { + str.close(); + } catch (IOException e) { + Log.e(LOG_TAG, "Failed to close file output stream", e); + } + if (!mNewName.renameTo(mBaseName)) { + Log.e(LOG_TAG, "Failed to rename new file " + mNewName + " to base file " + mBaseName); + } + if (mCommitTag != null) { + com.android.internal.logging.EventLogTags.writeCommitSysConfigFile( + mCommitTag, SystemClock.uptimeMillis() - mStartTime); } } /** * Call when you have failed for some reason at writing to the stream * returned by {@link #startWrite()}. This will close the current - * write stream, and roll back to the previous state of the file. + * write stream, and delete the new file. */ public void failWrite(FileOutputStream str) { - if (str != null) { - FileUtils.sync(str); - try { - str.close(); - mBaseName.delete(); - mBackupName.renameTo(mBaseName); - } catch (IOException e) { - Log.w("AtomicFile", "failWrite: Got exception:", e); - } + if (str == null) { + return; + } + if (!FileUtils.sync(str)) { + Log.e(LOG_TAG, "Failed to sync file output stream"); + } + try { + str.close(); + } catch (IOException e) { + Log.e(LOG_TAG, "Failed to close file output stream", e); + } + if (!mNewName.delete()) { + Log.e(LOG_TAG, "Failed to delete new file " + mNewName); } } @@ -210,32 +214,34 @@ public class AtomicFile { } /** - * Open the atomic file for reading. If there previously was an - * incomplete write, this will roll back to the last good data before - * opening for read. You should call close() on the FileInputStream when - * you are done reading from it. - * - * <p>Note that if another thread is currently performing - * a write, this will incorrectly consider it to be in the state of a bad - * write and roll back, causing the new data currently being written to - * be dropped. You must do your own threading protection for access to - * AtomicFile. + * Open the atomic file for reading. You should call close() on the FileInputStream when you are + * done reading from it. + * <p> + * You must do your own threading protection for access to AtomicFile. */ public FileInputStream openRead() throws FileNotFoundException { - if (mBackupName.exists()) { - mBaseName.delete(); - mBackupName.renameTo(mBaseName); + if (mLegacyBackupName.exists()) { + if (!mLegacyBackupName.renameTo(mBaseName)) { + Log.e(LOG_TAG, "Failed to rename legacy backup file " + mLegacyBackupName + + " to base file " + mBaseName); + } + } + + if (mNewName.exists()) { + if (!mNewName.delete()) { + Log.e(LOG_TAG, "Failed to delete outdated new file " + mNewName); + } } return new FileInputStream(mBaseName); } /** * @hide - * Checks if the original or backup file exists. - * @return whether the original or backup file exists. + * Checks if the original or legacy backup file exists. + * @return whether the original or legacy backup file exists. */ public boolean exists() { - return mBaseName.exists() || mBackupName.exists(); + return mBaseName.exists() || mLegacyBackupName.exists(); } /** @@ -246,8 +252,8 @@ public class AtomicFile { * the file does not exist or an I/O error is encountered. */ public long getLastModifiedTime() { - if (mBackupName.exists()) { - return mBackupName.lastModified(); + if (mLegacyBackupName.exists()) { + return mLegacyBackupName.lastModified(); } return mBaseName.lastModified(); } diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index e224e84a56fe..91b9390745ef 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -1121,6 +1121,7 @@ public abstract class WebSettings { * @deprecated The Application Cache API is deprecated and this method will * become a no-op on all Android versions once support is * removed in Chromium. Consider using Service Workers instead. + * See https://web.dev/appcache-removal/ for more information. */ public abstract void setAppCacheEnabled(boolean flag); @@ -1136,6 +1137,7 @@ public abstract class WebSettings { * @deprecated The Application Cache API is deprecated and this method will * become a no-op on all Android versions once support is * removed in Chromium. Consider using Service Workers instead. + * See https://web.dev/appcache-removal/ for more information. */ public abstract void setAppCachePath(String appCachePath); diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java index 2568d097d404..f1b716143787 100644 --- a/core/java/com/android/internal/app/ChooserListAdapter.java +++ b/core/java/com/android/internal/app/ChooserListAdapter.java @@ -238,8 +238,9 @@ public class ChooserListAdapter extends ResolverListAdapter { } @Override - protected void onBindView(View view, TargetInfo info) { - super.onBindView(view, info); + protected void onBindView(View view, TargetInfo info, int position) { + super.onBindView(view, info, position); + if (info == null) return; // If target is loading, show a special placeholder shape in the label, make unclickable final ViewHolder holder = (ViewHolder) view.getTag(); @@ -257,11 +258,16 @@ public class ChooserListAdapter extends ResolverListAdapter { holder.itemView.setBackground(holder.defaultItemViewBackground); } - // If the target is grouped show an indicator if (info instanceof MultiDisplayResolveInfo) { + // If the target is grouped show an indicator Drawable bkg = mContext.getDrawable(R.drawable.chooser_group_background); holder.text.setPaddingRelative(0, 0, bkg.getIntrinsicWidth() /* end */, 0); holder.text.setBackground(bkg); + } else if (info.isPinned() && getPositionTargetType(position) == TARGET_STANDARD) { + // If the target is pinned and in the suggested row show a pinned indicator + Drawable bkg = mContext.getDrawable(R.drawable.chooser_pinned_background); + holder.text.setPaddingRelative(bkg.getIntrinsicWidth() /* start */, 0, 0, 0); + holder.text.setBackground(bkg); } else { holder.text.setBackground(null); holder.text.setPaddingRelative(0, 0, 0, 0); diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java index af9c0408ccaa..d942e859ccd0 100644 --- a/core/java/com/android/internal/app/ResolverListAdapter.java +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -524,7 +524,7 @@ public class ResolverListAdapter extends BaseAdapter { if (view == null) { view = createView(parent); } - onBindView(view, getItem(position)); + onBindView(view, getItem(position), position); return view; } @@ -541,10 +541,10 @@ public class ResolverListAdapter extends BaseAdapter { } public final void bindView(int position, View view) { - onBindView(view, getItem(position)); + onBindView(view, getItem(position), position); } - protected void onBindView(View view, TargetInfo info) { + protected void onBindView(View view, TargetInfo info, int position) { final ViewHolder holder = (ViewHolder) view.getTag(); if (info == null) { holder.icon.setImageDrawable( diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index a420ba67868f..7b708efdb278 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -652,6 +652,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p char dex2oatImageFlagsBuf[PROPERTY_VALUE_MAX]; char extraOptsBuf[PROPERTY_VALUE_MAX]; char voldDecryptBuf[PROPERTY_VALUE_MAX]; + char perfettoHprofOptBuf[sizeof("-XX:PerfettoHprof=") + PROPERTY_VALUE_MAX]; enum { kEMDefault, kEMIntPortable, @@ -766,6 +767,16 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p addOption("-verbose:gc"); //addOption("-verbose:class"); + // On Android, we always want to allow loading the PerfettoHprof plugin. + // Even with this option set, we will still only actually load the plugin + // if we are on a userdebug build or the app is debuggable or profileable. + // This is enforced in art/runtime/runtime.cc. + // + // We want to be able to disable this, because this does not work on host, + // and we do not want to enable it in tests. + parseRuntimeOption("dalvik.vm.perfetto_hprof", perfettoHprofOptBuf, "-XX:PerfettoHprof=", + "true"); + if (primary_zygote) { addOption("-Xprimaryzygote"); } diff --git a/core/proto/android/view/imefocuscontroller.proto b/core/proto/android/view/imefocuscontroller.proto new file mode 100644 index 000000000000..ff9dee69207b --- /dev/null +++ b/core/proto/android/view/imefocuscontroller.proto @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.view; + +option java_multiple_files = true; + +/** + * Represents a {@link android.view.ImeFocusController} object. + */ +message ImeFocusControllerProto { + optional bool has_ime_focus = 1; + optional string served_view = 2; + optional string next_served_view = 3; +}
\ No newline at end of file diff --git a/core/proto/android/view/imeinsetssourceconsumer.proto b/core/proto/android/view/imeinsetssourceconsumer.proto new file mode 100644 index 000000000000..680916345a31 --- /dev/null +++ b/core/proto/android/view/imeinsetssourceconsumer.proto @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto"; + +package android.view; + +option java_multiple_files = true; + +/** + * Represents a {@link android.view.ImeInsetsSourceConsumer} object. + */ +message ImeInsetsSourceConsumerProto { + optional .android.view.inputmethod.EditorInfoProto focused_editor = 1; + optional bool is_requested_visible_awaiting_control = 2; +}
\ No newline at end of file diff --git a/core/proto/android/view/inputmethod/editorinfo.proto b/core/proto/android/view/inputmethod/editorinfo.proto new file mode 100644 index 000000000000..f93096f9d395 --- /dev/null +++ b/core/proto/android/view/inputmethod/editorinfo.proto @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.view.inputmethod; + +option java_multiple_files = true; + +/** + * Represents a {@link android.view.inputmethod.EditorInfo} object. + */ +message EditorInfoProto { + optional int32 input_type = 1; + optional int32 ime_options = 2; + optional string private_ime_options = 3; + optional string package_name = 4; + optional int32 field_id = 5; + optional int32 target_input_method_user_id = 6; +}
\ No newline at end of file diff --git a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto new file mode 100644 index 000000000000..732213966014 --- /dev/null +++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +option java_outer_classname = "InputMethodEditorTraceProto"; + +package android.view.inputmethod; + +import "frameworks/base/core/proto/android/view/inputmethod/inputmethodmanager.proto"; +import "frameworks/base/core/proto/android/view/viewrootimpl.proto"; +import "frameworks/base/core/proto/android/view/insetscontroller.proto"; +import "frameworks/base/core/proto/android/view/insetssourceconsumer.proto"; +import "frameworks/base/core/proto/android/view/imeinsetssourceconsumer.proto"; +import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto"; +import "frameworks/base/core/proto/android/view/imefocuscontroller.proto"; + +/** + * Represents a file full of input method editor trace entries. + * Encoded, it should start with 0x9 0x49 0x4d 0x45 0x54 0x52 0x41 0x43 0x45 (.IMETRACE), such + * that they can be easily identified. + */ +message InputMethodEditorTraceFileProto { + + /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L + (this is needed because enums have to be 32 bits and there's no nice way to put 64bit + constants into .proto files.) */ + enum MagicNumber { + INVALID = 0; + MAGIC_NUMBER_L = 0x54454d49; /* IMET (little-endian ASCII) */ + MAGIC_NUMBER_H = 0x45434152; /* RACE (little-endian ASCII) */ + } + + /* Must be the first field to allow winscope to auto-detect the dump type. Set to value + in MagicNumber */ + optional fixed64 magic_number = 1; + repeated InputMethodEditorProto entry = 2; +} + +/* one input method editor dump entry. */ +message InputMethodEditorProto { + + /* required: elapsed realtime in nanos since boot of when this entry was logged */ + optional fixed64 elapsed_realtime_nanos = 1; + optional ClientSideProto client_side_dump = 2; + + /* groups together the dump from ime related client side classes */ + message ClientSideProto { + optional InputMethodManagerProto input_method_manager = 1; + optional ViewRootImplProto view_root_impl = 2; + optional InsetsControllerProto insets_controller = 3; + optional InsetsSourceConsumerProto insets_source_consumer = 4; + optional ImeInsetsSourceConsumerProto ime_insets_source_consumer = 5; + optional EditorInfoProto editor_info = 6; + optional ImeFocusControllerProto ime_focus_controller = 7; + } +}
\ No newline at end of file diff --git a/core/proto/android/view/inputmethod/inputmethodmanager.proto b/core/proto/android/view/inputmethod/inputmethodmanager.proto new file mode 100644 index 000000000000..9fed0ef95a27 --- /dev/null +++ b/core/proto/android/view/inputmethod/inputmethodmanager.proto @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.view.inputmethod; + +option java_multiple_files = true; + +/** + * Represents a {@link android.view.inputmethod.InputMethodManager} object. + */ +message InputMethodManagerProto { + optional string cur_id = 1; + optional bool fullscreen_mode = 2; + optional int32 display_id = 3; + optional bool active = 4; + optional bool served_connecting = 5; +}
\ No newline at end of file diff --git a/core/proto/android/view/insetsanimationcontrolimpl.proto b/core/proto/android/view/insetsanimationcontrolimpl.proto new file mode 100644 index 000000000000..6eec37b8298e --- /dev/null +++ b/core/proto/android/view/insetsanimationcontrolimpl.proto @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.view; + +option java_multiple_files = true; + +/** + * Represents a {@link android.view.InsetsAnimationControlImpl} object. + */ +message InsetsAnimationControlImplProto { + optional bool is_cancelled = 1; + optional bool is_finished = 2; + optional string tmp_matrix = 3; + optional string pending_insets = 4; + optional float pending_fraction = 5; + optional bool shown_on_finish = 6; + optional float current_alpha = 7; + optional float pending_alpha = 8; +}
\ No newline at end of file diff --git a/core/proto/android/view/insetscontroller.proto b/core/proto/android/view/insetscontroller.proto new file mode 100644 index 000000000000..a8bf431ce156 --- /dev/null +++ b/core/proto/android/view/insetscontroller.proto @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +import "frameworks/base/core/proto/android/view/insetsstate.proto"; +import "frameworks/base/core/proto/android/view/insetsanimationcontrolimpl.proto"; + +package android.view; + +option java_multiple_files = true; + +/** + * Represents a {@link android.view.InsetsController} object. + */ +message InsetsControllerProto { + optional InsetsStateProto state = 1; + repeated InsetsAnimationControlImplProto control = 2; +}
\ No newline at end of file diff --git a/core/proto/android/view/insetssource.proto b/core/proto/android/view/insetssource.proto new file mode 100644 index 000000000000..41b9f432a0ed --- /dev/null +++ b/core/proto/android/view/insetssource.proto @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +import "frameworks/base/core/proto/android/graphics/rect.proto"; + +package android.view; + +option java_multiple_files = true; + +/** + * Represents a {@link android.view.InsetsSource} object. + */ +message InsetsSourceProto { + optional string type = 1; + optional .android.graphics.RectProto frame = 2; + optional .android.graphics.RectProto visible_frame = 3; + optional bool visible = 4; +}
\ No newline at end of file diff --git a/core/proto/android/view/insetssourceconsumer.proto b/core/proto/android/view/insetssourceconsumer.proto new file mode 100644 index 000000000000..487e06c1ccdf --- /dev/null +++ b/core/proto/android/view/insetssourceconsumer.proto @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +import "frameworks/base/core/proto/android/view/insetssourcecontrol.proto"; +import "frameworks/base/core/proto/android/graphics/rect.proto"; + +package android.view; + +option java_multiple_files = true; + +/** + * Represents a {@link android.view.InsetsSourceConsumer} object. + */ +message InsetsSourceConsumerProto { + optional string internal_insets_type = 1; + optional bool has_window_focus = 2; + optional bool is_requested_visible = 3; + optional InsetsSourceControlProto source_control = 4; + optional .android.graphics.RectProto pending_frame = 5; + optional .android.graphics.RectProto pending_visible_frame = 6; +}
\ No newline at end of file diff --git a/core/proto/android/view/insetssourcecontrol.proto b/core/proto/android/view/insetssourcecontrol.proto new file mode 100644 index 000000000000..3ac3cbfafaff --- /dev/null +++ b/core/proto/android/view/insetssourcecontrol.proto @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +import "frameworks/base/core/proto/android/graphics/point.proto"; +import "frameworks/base/core/proto/android/view/surfacecontrol.proto"; + +package android.view; + +option java_multiple_files = true; + +/** + * Represents a {@link android.view.InsetsSourceControl} object. + */ +message InsetsSourceControlProto { + optional string type = 1; + optional .android.graphics.PointProto position = 2; + optional SurfaceControlProto leash = 3; +}
\ No newline at end of file diff --git a/core/proto/android/view/insetsstate.proto b/core/proto/android/view/insetsstate.proto new file mode 100644 index 000000000000..9e9933d72c6c --- /dev/null +++ b/core/proto/android/view/insetsstate.proto @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +import "frameworks/base/core/proto/android/view/insetssource.proto"; +import "frameworks/base/core/proto/android/graphics/rect.proto"; + +package android.view; + +option java_multiple_files = true; + +/** + * Represents a {@link android.view.InsetsState} object. + */ +message InsetsStateProto { + repeated InsetsSourceProto sources = 1; + optional .android.graphics.RectProto display_frame = 2; +}
\ No newline at end of file diff --git a/core/proto/android/view/viewrootimpl.proto b/core/proto/android/view/viewrootimpl.proto new file mode 100644 index 000000000000..0abe5e0624e3 --- /dev/null +++ b/core/proto/android/view/viewrootimpl.proto @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +import "frameworks/base/core/proto/android/graphics/rect.proto"; +import "frameworks/base/core/proto/android/view/displaycutout.proto"; +import "frameworks/base/core/proto/android/view/windowlayoutparams.proto"; + +package android.view; + +option java_multiple_files = true; + +/** + * Represents a {@link android.view.ViewRootImpl} object. + */ +message ViewRootImplProto { + optional string view = 1; + optional int32 display_id = 2; + optional bool app_visible = 3; + optional int32 width = 4; + optional int32 height = 5; + optional bool is_animating = 6; + optional .android.graphics.RectProto visible_rect = 7; + optional bool is_drawing = 8; + optional bool added = 9; + optional .android.graphics.RectProto win_frame = 10; + optional DisplayCutoutProto pending_display_cutout = 11; + optional string last_window_insets = 12; + optional string soft_input_mode = 13; + optional int32 scroll_y = 14; + optional int32 cur_scroll_y = 15; + optional bool removed = 16; + optional .android.view.WindowLayoutParamsProto window_attributes = 17; +}
\ No newline at end of file diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 3cd0f03de727..4bc570ad0adb 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5474,7 +5474,8 @@ android:permission="android.permission.BIND_JOB_SERVICE"> </service> - <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"> + <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader" + android:exported="false"> <intent-filter> <action android:name="android.intent.action.LOAD_DATA" /> </intent-filter> diff --git a/core/res/res/drawable/chooser_group_background.xml b/core/res/res/drawable/chooser_group_background.xml index 2bf9337557ed..036028de7bcb 100644 --- a/core/res/res/drawable/chooser_group_background.xml +++ b/core/res/res/drawable/chooser_group_background.xml @@ -21,5 +21,5 @@ android:width="12dp" android:height="12dp" android:start="4dp" - android:end="4dp" /> + android:end="0dp" /> </layer-list> diff --git a/core/res/res/drawable/chooser_pinned_background.xml b/core/res/res/drawable/chooser_pinned_background.xml new file mode 100644 index 000000000000..fbbe8c107fb9 --- /dev/null +++ b/core/res/res/drawable/chooser_pinned_background.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="@drawable/ic_chooser_pin" + android:gravity="start|center_vertical" + android:width="12dp" + android:height="12dp" + android:start="0dp" + android:end="4dp" /> +</layer-list>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_chooser_pin.xml b/core/res/res/drawable/ic_chooser_pin.xml new file mode 100644 index 000000000000..47851dcbf5bd --- /dev/null +++ b/core/res/res/drawable/ic_chooser_pin.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="12dp" + android:height="12dp" + android:viewportWidth="12" + android:viewportHeight="12" + android:tint="?attr/textColorSecondary"> + <path + android:pathData="M8.5,2C8.5,1.45 8.055,1 7.5,1L4.5,1C3.95,1 3.5,1.45 3.5,2L3.5,5.5L2.5,7L2.5,8L5.5,8L5.5,10.5L6,11L6.5,10.5L6.5,8L9.5,8L9.5,7L8.5,5.5L8.5,2Z" + android:fillColor="#FF000000" /> +</vector> diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml index fdd965f3b157..50e6f33f628a 100644 --- a/core/res/res/layout/resolve_grid_item.xml +++ b/core/res/res/layout/resolve_grid_item.xml @@ -44,7 +44,7 @@ android:layout_height="wrap_content" android:textAppearance="?attr/textAppearanceSmall" android:textColor="?attr/textColorPrimary" - android:textSize="14sp" + android:textSize="12sp" android:gravity="top|center_horizontal" android:lines="1" android:ellipsize="end" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index b62bb33256dc..fe7b0ae83c01 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -44,6 +44,7 @@ <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item> <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item> <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_screen_record</xliff:g></item> <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item> <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item> <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item> @@ -59,7 +60,6 @@ <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item> <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item> <item><xliff:g id="id">@string/status_bar_sensors_off</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_screen_record</xliff:g></item> </string-array> <string translatable="false" name="status_bar_rotate">rotate</string> @@ -3329,6 +3329,17 @@ <!-- Controls the size of the back gesture inset. --> <dimen name="config_backGestureInset">0dp</dimen> + <!-- Array of values used in Gesture Navigation settings page to reduce/increase the back + gesture's inset size. These values will be multiplied into the default width, read from the + gesture navigation overlay package, in order to create 4 different sizes which are selectable + via a slider component. --> + <array name="config_backGestureInsetScales"> + <item>0.75</item> + <item>1.00</item> + <item>1.33</item> + <item>1.66</item> + </array> + <!-- Controls whether the navbar needs a scrim with {@link Window#setEnsuringNavigationBarContrastWhenTransparent}. --> <bool name="config_navBarNeedsScrim">true</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b483ee04a7ec..7744e9e9a8a7 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2747,6 +2747,8 @@ <java-symbol type="dimen" name="chooser_preview_image_max_dimen"/> <java-symbol type="drawable" name="ic_chooser_group_arrow"/> <java-symbol type="drawable" name="chooser_group_background"/> + <java-symbol type="drawable" name="ic_chooser_pin"/> + <java-symbol type="drawable" name="chooser_pinned_background"/> <java-symbol type="integer" name="config_maxShortcutTargetsPerApp" /> <java-symbol type="layout" name="resolve_grid_item" /> <java-symbol type="id" name="day_picker_view_pager" /> @@ -2816,6 +2818,7 @@ <java-symbol type="bool" name="config_navBarNeedsScrim" /> <java-symbol type="bool" name="config_allowSeamlessRotationDespiteNavBarMoving" /> <java-symbol type="dimen" name="config_backGestureInset" /> + <java-symbol type="array" name="config_backGestureInsetScales" /> <java-symbol type="color" name="system_bar_background_semi_transparent" /> <java-symbol type="bool" name="config_showGesturalNavigationHints" /> diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index a93dacf47050..000e870369db 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -20,6 +20,7 @@ import static android.content.Intent.ACTION_EDIT; import static android.content.Intent.ACTION_VIEW; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.view.Display.INVALID_DISPLAY; import static com.google.common.truth.Truth.assertThat; @@ -29,6 +30,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.testng.Assert.assertFalse; +import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityThread; import android.app.IApplicationThread; @@ -38,6 +40,7 @@ import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ActivityRelaunchItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.ClientTransactionItem; +import android.app.servertransaction.ConfigurationChangeItem; import android.app.servertransaction.NewIntentItem; import android.app.servertransaction.ResumeActivityItem; import android.app.servertransaction.StopActivityItem; @@ -225,7 +228,7 @@ public class ActivityThreadTest { } @Test - public void testHandleActivityConfigurationChanged_PickNewerPendingConfiguration() { + public void testHandleActivityConfigurationChanged_SkipWhenNewerConfigurationPending() { final TestActivity activity = mActivityTestRule.launchActivity(new Intent()); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { @@ -237,23 +240,88 @@ public class ActivityThreadTest { final ActivityThread activityThread = activity.getActivityThread(); - final Configuration pendingConfig = new Configuration(); - pendingConfig.orientation = orientation == ORIENTATION_LANDSCAPE - ? ORIENTATION_PORTRAIT - : ORIENTATION_LANDSCAPE; - pendingConfig.seq = seq + 2; + final Configuration newerConfig = new Configuration(); + newerConfig.orientation = orientation == ORIENTATION_LANDSCAPE + ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; + newerConfig.seq = seq + 2; activityThread.updatePendingActivityConfiguration(activity.getActivityToken(), - pendingConfig); + newerConfig); - final Configuration newConfig = new Configuration(); - newConfig.orientation = orientation; - newConfig.seq = seq + 1; + final Configuration olderConfig = new Configuration(); + olderConfig.orientation = orientation; + olderConfig.seq = seq + 1; activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), - newConfig, Display.INVALID_DISPLAY); + olderConfig, INVALID_DISPLAY); + assertEquals(numOfConfig, activity.mNumOfConfigChanges); + assertEquals(olderConfig.orientation, activity.mConfig.orientation); + + activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), + newerConfig, INVALID_DISPLAY); assertEquals(numOfConfig + 1, activity.mNumOfConfigChanges); - assertEquals(pendingConfig.orientation, activity.mConfig.orientation); + assertEquals(newerConfig.orientation, activity.mConfig.orientation); + }); + } + + @Test + public void testHandleActivityConfigurationChanged_EnsureUpdatesProcessedInOrder() + throws Exception { + final TestActivity activity = mActivityTestRule.launchActivity(new Intent()); + + final ActivityThread activityThread = activity.getActivityThread(); + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + final Configuration config = new Configuration(); + config.seq = BASE_SEQ; + config.orientation = ORIENTATION_PORTRAIT; + + activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), + config, INVALID_DISPLAY); }); + + final IApplicationThread appThread = activityThread.getApplicationThread(); + final int numOfConfig = activity.mNumOfConfigChanges; + + final Configuration processConfigLandscape = new Configuration(); + processConfigLandscape.windowConfiguration.setBounds(new Rect(0, 0, 100, 60)); + processConfigLandscape.seq = BASE_SEQ + 1; + + final Configuration activityConfigLandscape = new Configuration(); + activityConfigLandscape.windowConfiguration.setBounds(new Rect(0, 0, 100, 50)); + activityConfigLandscape.seq = BASE_SEQ + 2; + + final Configuration processConfigPortrait = new Configuration(); + processConfigPortrait.windowConfiguration.setBounds(new Rect(0, 0, 60, 100)); + processConfigPortrait.seq = BASE_SEQ + 3; + + final Configuration activityConfigPortrait = new Configuration(); + activityConfigPortrait.windowConfiguration.setBounds(new Rect(0, 0, 50, 100)); + activityConfigPortrait.seq = BASE_SEQ + 4; + + activity.mConfigLatch = new CountDownLatch(1); + activity.mTestLatch = new CountDownLatch(1); + + ClientTransaction transaction = newTransaction(activityThread, null); + transaction.addCallback(ConfigurationChangeItem.obtain(processConfigLandscape)); + appThread.scheduleTransaction(transaction); + + transaction = newTransaction(activityThread, activity.getActivityToken()); + transaction.addCallback(ActivityConfigurationChangeItem.obtain(activityConfigLandscape)); + transaction.addCallback(ConfigurationChangeItem.obtain(processConfigPortrait)); + transaction.addCallback(ActivityConfigurationChangeItem.obtain(activityConfigPortrait)); + appThread.scheduleTransaction(transaction); + + activity.mTestLatch.await(); + activity.mConfigLatch.countDown(); + + activity.mConfigLatch = null; + activity.mTestLatch = null; + + // Check display metrics, bounds should match the portrait activity bounds. + final Rect bounds = activity.getWindowManager().getCurrentWindowMetrics().getBounds(); + assertEquals(activityConfigPortrait.windowConfiguration.getBounds(), bounds); + + // Ensure that Activity#onConfigurationChanged() is only called once. + assertEquals(numOfConfig + 1, activity.mNumOfConfigChanges); } @Test @@ -268,7 +336,7 @@ public class ActivityThreadTest { config.orientation = ORIENTATION_PORTRAIT; activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), - config, Display.INVALID_DISPLAY); + config, INVALID_DISPLAY); }); final int numOfConfig = activity.mNumOfConfigChanges; @@ -504,7 +572,7 @@ public class ActivityThreadTest { config.orientation = ORIENTATION_PORTRAIT; config.seq = seq; activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), config, - Display.INVALID_DISPLAY); + INVALID_DISPLAY); if (activity.mNumOfConfigChanges > numOfConfig) { return config.seq; @@ -514,7 +582,7 @@ public class ActivityThreadTest { config.orientation = ORIENTATION_LANDSCAPE; config.seq = seq + 1; activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), config, - Display.INVALID_DISPLAY); + INVALID_DISPLAY); return config.seq; } @@ -572,8 +640,12 @@ public class ActivityThreadTest { } private static ClientTransaction newTransaction(Activity activity) { - final IApplicationThread appThread = activity.getActivityThread().getApplicationThread(); - return ClientTransaction.obtain(appThread, activity.getActivityToken()); + return newTransaction(activity.getActivityThread(), activity.getActivityToken()); + } + + private static ClientTransaction newTransaction(ActivityThread activityThread, + @Nullable IBinder activityToken) { + return ClientTransaction.obtain(activityThread.getApplicationThread(), activityToken); } // Test activity diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index 6c23125aaf13..4654f63a2a91 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -63,7 +63,8 @@ public class ObjectPoolTests { @Test public void testRecycleActivityConfigurationChangeItem() { - ActivityConfigurationChangeItem emptyItem = ActivityConfigurationChangeItem.obtain(null); + ActivityConfigurationChangeItem emptyItem = + ActivityConfigurationChangeItem.obtain(Configuration.EMPTY); ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(config()); assertNotSame(item, emptyItem); assertFalse(item.equals(emptyItem)); @@ -186,7 +187,7 @@ public class ObjectPoolTests { @Test public void testRecycleMoveToDisplayItem() { - MoveToDisplayItem emptyItem = MoveToDisplayItem.obtain(0, null); + MoveToDisplayItem emptyItem = MoveToDisplayItem.obtain(0, Configuration.EMPTY); MoveToDisplayItem item = MoveToDisplayItem.obtain(4, config()); assertNotSame(item, emptyItem); assertFalse(item.equals(emptyItem)); diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 3f8d9ef964db..f11adef81793 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -563,6 +563,11 @@ public class TransactionParcelTests { } @Override + public void dumpCacheInfo(ParcelFileDescriptor parcelFileDescriptor, String[] strings) + throws RemoteException { + } + + @Override public void dumpProvider(ParcelFileDescriptor parcelFileDescriptor, IBinder iBinder, String[] strings) throws RemoteException { } diff --git a/core/tests/utiltests/src/android/util/AtomicFileTest.java b/core/tests/utiltests/src/android/util/AtomicFileTest.java new file mode 100644 index 000000000000..547d4d844181 --- /dev/null +++ b/core/tests/utiltests/src/android/util/AtomicFileTest.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import static org.junit.Assert.assertArrayEquals; + +import android.app.Instrumentation; +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +@RunWith(Parameterized.class) +public class AtomicFileTest { + private static final String BASE_NAME = "base"; + private static final String NEW_NAME = BASE_NAME + ".new"; + private static final String LEGACY_BACKUP_NAME = BASE_NAME + ".bak"; + + private enum WriteAction { + FINISH, + FAIL, + ABORT + } + + private static final byte[] BASE_BYTES = "base".getBytes(StandardCharsets.UTF_8); + private static final byte[] EXISTING_NEW_BYTES = "unnew".getBytes(StandardCharsets.UTF_8); + private static final byte[] NEW_BYTES = "new".getBytes(StandardCharsets.UTF_8); + private static final byte[] LEGACY_BACKUP_BYTES = "bak".getBytes(StandardCharsets.UTF_8); + + // JUnit wants every parameter to be used so make it happy. + @Parameterized.Parameter() + public String mUnusedTestName; + @Nullable + @Parameterized.Parameter(1) + public String[] mExistingFileNames; + @Nullable + @Parameterized.Parameter(2) + public WriteAction mWriteAction; + @Nullable + @Parameterized.Parameter(3) + public byte[] mExpectedBytes; + + private final Instrumentation mInstrumentation = + InstrumentationRegistry.getInstrumentation(); + private final Context mContext = mInstrumentation.getContext(); + + private final File mDirectory = mContext.getFilesDir(); + private final File mBaseFile = new File(mDirectory, BASE_NAME); + private final File mNewFile = new File(mDirectory, NEW_NAME); + private final File mLegacyBackupFile = new File(mDirectory, LEGACY_BACKUP_NAME); + + @Parameterized.Parameters(name = "{0}") + public static Object[][] data() { + return new Object[][] { + { "none + none = none", null, null, null }, + { "none + finish = new", null, WriteAction.FINISH, NEW_BYTES }, + { "none + fail = none", null, WriteAction.FAIL, null }, + { "none + abort = none", null, WriteAction.ABORT, null }, + { "base + none = base", new String[] { BASE_NAME }, null, BASE_BYTES }, + { "base + finish = new", new String[] { BASE_NAME }, WriteAction.FINISH, + NEW_BYTES }, + { "base + fail = base", new String[] { BASE_NAME }, WriteAction.FAIL, BASE_BYTES }, + { "base + abort = base", new String[] { BASE_NAME }, WriteAction.ABORT, + BASE_BYTES }, + { "new + none = none", new String[] { NEW_NAME }, null, null }, + { "new + finish = new", new String[] { NEW_NAME }, WriteAction.FINISH, NEW_BYTES }, + { "new + fail = none", new String[] { NEW_NAME }, WriteAction.FAIL, null }, + { "new + abort = none", new String[] { NEW_NAME }, WriteAction.ABORT, null }, + { "bak + none = bak", new String[] { LEGACY_BACKUP_NAME }, null, + LEGACY_BACKUP_BYTES }, + { "bak + finish = new", new String[] { LEGACY_BACKUP_NAME }, WriteAction.FINISH, + NEW_BYTES }, + { "bak + fail = bak", new String[] { LEGACY_BACKUP_NAME }, WriteAction.FAIL, + LEGACY_BACKUP_BYTES }, + { "bak + abort = bak", new String[] { LEGACY_BACKUP_NAME }, WriteAction.ABORT, + LEGACY_BACKUP_BYTES }, + { "base & new + none = base", new String[] { BASE_NAME, NEW_NAME }, null, + BASE_BYTES }, + { "base & new + finish = new", new String[] { BASE_NAME, NEW_NAME }, + WriteAction.FINISH, NEW_BYTES }, + { "base & new + fail = base", new String[] { BASE_NAME, NEW_NAME }, + WriteAction.FAIL, BASE_BYTES }, + { "base & new + abort = base", new String[] { BASE_NAME, NEW_NAME }, + WriteAction.ABORT, BASE_BYTES }, + { "base & bak + none = bak", new String[] { BASE_NAME, LEGACY_BACKUP_NAME }, null, + LEGACY_BACKUP_BYTES }, + { "base & bak + finish = new", new String[] { BASE_NAME, LEGACY_BACKUP_NAME }, + WriteAction.FINISH, NEW_BYTES }, + { "base & bak + fail = bak", new String[] { BASE_NAME, LEGACY_BACKUP_NAME }, + WriteAction.FAIL, LEGACY_BACKUP_BYTES }, + { "base & bak + abort = bak", new String[] { BASE_NAME, LEGACY_BACKUP_NAME }, + WriteAction.ABORT, LEGACY_BACKUP_BYTES }, + { "new & bak + none = bak", new String[] { NEW_NAME, LEGACY_BACKUP_NAME }, null, + LEGACY_BACKUP_BYTES }, + { "new & bak + finish = new", new String[] { NEW_NAME, LEGACY_BACKUP_NAME }, + WriteAction.FINISH, NEW_BYTES }, + { "new & bak + fail = bak", new String[] { NEW_NAME, LEGACY_BACKUP_NAME }, + WriteAction.FAIL, LEGACY_BACKUP_BYTES }, + { "new & bak + abort = bak", new String[] { NEW_NAME, LEGACY_BACKUP_NAME }, + WriteAction.ABORT, LEGACY_BACKUP_BYTES }, + { "base & new & bak + none = bak", + new String[] { BASE_NAME, NEW_NAME, LEGACY_BACKUP_NAME }, null, + LEGACY_BACKUP_BYTES }, + { "base & new & bak + finish = new", + new String[] { BASE_NAME, NEW_NAME, LEGACY_BACKUP_NAME }, + WriteAction.FINISH, NEW_BYTES }, + { "base & new & bak + fail = bak", + new String[] { BASE_NAME, NEW_NAME, LEGACY_BACKUP_NAME }, WriteAction.FAIL, + LEGACY_BACKUP_BYTES }, + { "base & new & bak + abort = bak", + new String[] { BASE_NAME, NEW_NAME, LEGACY_BACKUP_NAME }, WriteAction.ABORT, + LEGACY_BACKUP_BYTES }, + }; + } + + @Before + @After + public void deleteFiles() { + mBaseFile.delete(); + mNewFile.delete(); + mLegacyBackupFile.delete(); + } + + @Test + public void testAtomicFile() throws Exception { + if (mExistingFileNames != null) { + for (String fileName : mExistingFileNames) { + switch (fileName) { + case BASE_NAME: + writeBytes(mBaseFile, BASE_BYTES); + break; + case NEW_NAME: + writeBytes(mNewFile, EXISTING_NEW_BYTES); + break; + case LEGACY_BACKUP_NAME: + writeBytes(mLegacyBackupFile, LEGACY_BACKUP_BYTES); + break; + default: + throw new AssertionError(fileName); + } + } + } + + AtomicFile atomicFile = new AtomicFile(mBaseFile); + if (mWriteAction != null) { + try (FileOutputStream outputStream = atomicFile.startWrite()) { + outputStream.write(NEW_BYTES); + switch (mWriteAction) { + case FINISH: + atomicFile.finishWrite(outputStream); + break; + case FAIL: + atomicFile.failWrite(outputStream); + break; + case ABORT: + // Neither finishing nor failing is called upon abort. + break; + default: + throw new AssertionError(mWriteAction); + } + } + } + + if (mExpectedBytes != null) { + try (FileInputStream inputStream = atomicFile.openRead()) { + assertArrayEquals(mExpectedBytes, readAllBytes(inputStream)); + } + } else { + assertThrows(FileNotFoundException.class, () -> atomicFile.openRead()); + } + } + + private static void writeBytes(@NonNull File file, @NonNull byte[] bytes) throws IOException { + try (FileOutputStream outputStream = new FileOutputStream(file)) { + outputStream.write(bytes); + } + } + + // InputStream.readAllBytes() is introduced in Java 9. Our files are small enough so that a + // naive implementation is okay. + private static byte[] readAllBytes(@NonNull InputStream inputStream) throws IOException { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + int b; + while ((b = inputStream.read()) != -1) { + outputStream.write(b); + } + return outputStream.toByteArray(); + } + } + + @NonNull + public static <T extends Throwable> T assertThrows(@NonNull Class<T> expectedType, + @NonNull ThrowingRunnable runnable) { + try { + runnable.run(); + } catch (Throwable t) { + if (!expectedType.isInstance(t)) { + sneakyThrow(t); + } + //noinspection unchecked + return (T) t; + } + throw new AssertionError(String.format("Expected %s wasn't thrown", + expectedType.getSimpleName())); + } + + private static <T extends Throwable> void sneakyThrow(@NonNull Throwable throwable) throws T { + //noinspection unchecked + throw (T) throwable; + } + + private interface ThrowingRunnable { + void run() throws Throwable; + } +} diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml index 72827a956f78..a5a2221e5532 100644 --- a/data/etc/com.android.systemui.xml +++ b/data/etc/com.android.systemui.xml @@ -16,6 +16,7 @@ --> <permissions> <privapp-permissions package="com.android.systemui"> + <permission name="android.permission.CAPTURE_AUDIO_OUTPUT"/> <permission name="android.permission.BATTERY_STATS"/> <permission name="android.permission.BIND_APPWIDGET"/> <permission name="android.permission.BLUETOOTH_PRIVILEGED"/> diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java index bbd739941422..54675d018038 100644 --- a/media/java/android/media/MediaMuxer.java +++ b/media/java/android/media/MediaMuxer.java @@ -321,6 +321,21 @@ final public class MediaMuxer { @UnsupportedAppUsage private long mNativeObject; + private String convertMuxerStateCodeToString(int aState) { + switch (aState) { + case MUXER_STATE_UNINITIALIZED: + return "UNINITIALIZED"; + case MUXER_STATE_INITIALIZED: + return "INITIALIZED"; + case MUXER_STATE_STARTED: + return "STARTED"; + case MUXER_STATE_STOPPED: + return "STOPPED"; + default: + return "UNKNOWN"; + } + } + /** * Constructor. * Creates a media muxer that writes to the specified path. @@ -397,7 +412,7 @@ final public class MediaMuxer { nativeSetOrientationHint(mNativeObject, degrees); } else { throw new IllegalStateException("Can't set rotation degrees due" + - " to wrong state."); + " to wrong state(" + convertMuxerStateCodeToString(mState) + ")"); } } @@ -432,7 +447,8 @@ final public class MediaMuxer { if (mState == MUXER_STATE_INITIALIZED && mNativeObject != 0) { nativeSetLocation(mNativeObject, latitudex10000, longitudex10000); } else { - throw new IllegalStateException("Can't set location due to wrong state."); + throw new IllegalStateException("Can't set location due to wrong state(" + + convertMuxerStateCodeToString(mState) + ")"); } } @@ -451,7 +467,8 @@ final public class MediaMuxer { nativeStart(mNativeObject); mState = MUXER_STATE_STARTED; } else { - throw new IllegalStateException("Can't start due to wrong state."); + throw new IllegalStateException("Can't start due to wrong state(" + + convertMuxerStateCodeToString(mState) + ")"); } } @@ -462,10 +479,16 @@ final public class MediaMuxer { */ public void stop() { if (mState == MUXER_STATE_STARTED) { - nativeStop(mNativeObject); - mState = MUXER_STATE_STOPPED; + try { + nativeStop(mNativeObject); + } catch (Exception e) { + throw e; + } finally { + mState = MUXER_STATE_STOPPED; + } } else { - throw new IllegalStateException("Can't stop due to wrong state."); + throw new IllegalStateException("Can't stop due to wrong state(" + + convertMuxerStateCodeToString(mState) + ")"); } } diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 50af60a0ad92..a458b16c551a 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.app.ActivityManager; import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.TvInputService; @@ -55,6 +56,8 @@ import android.os.Looper; import android.os.Message; import android.util.Log; +import com.android.internal.util.FrameworkStatsLog; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -208,7 +211,7 @@ public class Tuner implements AutoCloseable { private FrontendInfo mFrontendInfo; private Integer mFrontendHandle; private int mFrontendType = FrontendSettings.TYPE_UNDEFINED; - + private int mUserId; private Lnb mLnb; private Integer mLnbHandle; @Nullable @@ -232,6 +235,11 @@ public class Tuner implements AutoCloseable { new TunerResourceManager.ResourcesReclaimListener() { @Override public void onReclaimResources() { + if (mFrontend != null) { + FrameworkStatsLog + .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, + FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN); + } mHandler.sendMessage(mHandler.obtainMessage(MSG_RESOURCE_LOST)); } }; @@ -261,6 +269,8 @@ public class Tuner implements AutoCloseable { profile, new HandlerExecutor(mHandler), mResourceListener, clientId); mClientId = clientId[0]; + mUserId = ActivityManager.getCurrentUser(); + setFrontendInfoList(); setLnbIds(); } @@ -358,6 +368,9 @@ public class Tuner implements AutoCloseable { TunerUtils.throwExceptionForResult(res, "failed to close frontend"); } mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId); + FrameworkStatsLog + .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, + FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN); mFrontendHandle = null; mFrontend = null; } @@ -557,9 +570,14 @@ public class Tuner implements AutoCloseable { */ @Result public int tune(@NonNull FrontendSettings settings) { + Log.d(TAG, "Tune to " + settings.getFrequency()); mFrontendType = settings.getType(); if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { mFrontendInfo = null; + Log.d(TAG, "Write Stats Log for tuning."); + FrameworkStatsLog + .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, + FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__TUNING); return nativeTune(settings.getType(), settings); } return RESULT_UNAVAILABLE; @@ -602,6 +620,9 @@ public class Tuner implements AutoCloseable { mScanCallback = scanCallback; mScanCallbackExecutor = executor; mFrontendInfo = null; + FrameworkStatsLog + .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, + FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__SCANNING); return nativeScan(settings.getType(), settings, scanType); } return RESULT_UNAVAILABLE; @@ -620,6 +641,10 @@ public class Tuner implements AutoCloseable { */ @Result public int cancelScanning() { + FrameworkStatsLog + .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, + FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__SCAN_STOPPED); + int retVal = nativeStopScan(); mScanCallback = null; mScanCallbackExecutor = null; @@ -779,12 +804,33 @@ public class Tuner implements AutoCloseable { } private void onFrontendEvent(int eventType) { + Log.d(TAG, "Got event from tuning. Event type: " + eventType); if (mOnTunerEventExecutor != null && mOnTuneEventListener != null) { mOnTunerEventExecutor.execute(() -> mOnTuneEventListener.onTuneEvent(eventType)); } + + Log.d(TAG, "Wrote Stats Log for the events from tuning."); + if (eventType == OnTuneEventListener.SIGNAL_LOCKED) { + FrameworkStatsLog + .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, + FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__LOCKED); + } else if (eventType == OnTuneEventListener.SIGNAL_NO_SIGNAL) { + FrameworkStatsLog + .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, + FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__NOT_LOCKED); + } else if (eventType == OnTuneEventListener.SIGNAL_LOST_LOCK) { + FrameworkStatsLog + .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, + FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__SIGNAL_LOST); + } } private void onLocked() { + Log.d(TAG, "Wrote Stats Log for locked event from scanning."); + FrameworkStatsLog + .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, + FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__LOCKED); + if (mScanCallbackExecutor != null && mScanCallback != null) { mScanCallbackExecutor.execute(() -> mScanCallback.onLocked()); } diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 331477f72aa4..43cb25f03fb0 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -43,7 +43,7 @@ #include <android_runtime/android_hardware_HardwareBuffer.h> -#include <binder/MemoryHeapBase.h> +#include <binder/MemoryDealer.h> #include <cutils/compiler.h> @@ -306,6 +306,7 @@ status_t JMediaCodec::configure( CHECK(format->findString("mime", &mime)); mGraphicOutput = (mime.startsWithIgnoreCase("video/") || mime.startsWithIgnoreCase("image/")) && !(flags & CONFIGURE_FLAG_ENCODE); + mHasCryptoOrDescrambler = (crypto != nullptr) || (descrambler != nullptr); return mCodec->configure( format, mSurfaceTextureClient, crypto, descrambler, flags); @@ -1603,14 +1604,13 @@ struct NativeCryptoInfo { ScopedLocalRef<jobject> patternObj{ env, env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID)}; - CryptoPlugin::Pattern pattern; if (patternObj.get() == nullptr) { - pattern.mEncryptBlocks = 0; - pattern.mSkipBlocks = 0; + mPattern.mEncryptBlocks = 0; + mPattern.mSkipBlocks = 0; } else { - pattern.mEncryptBlocks = env->GetIntField( + mPattern.mEncryptBlocks = env->GetIntField( patternObj.get(), gFields.patternEncryptBlocksID); - pattern.mSkipBlocks = env->GetIntField( + mPattern.mSkipBlocks = env->GetIntField( patternObj.get(), gFields.patternSkipBlocksID); } @@ -1679,6 +1679,18 @@ struct NativeCryptoInfo { mIv = env->GetByteArrayElements(mIvObj.get(), nullptr); } } + + } + + explicit NativeCryptoInfo(jint size) + : mIvObj{nullptr, nullptr}, + mKeyObj{nullptr, nullptr}, + mMode{CryptoPlugin::kMode_Unencrypted}, + mPattern{0, 0} { + mSubSamples = new CryptoPlugin::SubSample[1]; + mNumSubSamples = 1; + mSubSamples[0].mNumBytesOfClearData = size; + mSubSamples[0].mNumBytesOfEncryptedData = 0; } ~NativeCryptoInfo() { @@ -2128,10 +2140,13 @@ static void android_media_MediaCodec_native_queueLinearBlock( if (env->GetBooleanField(bufferObj, gLinearBlockInfo.validId)) { JMediaCodecLinearBlock *context = (JMediaCodecLinearBlock *)env->GetLongField(bufferObj, gLinearBlockInfo.contextId); - if (cryptoInfoObj != nullptr) { + if (codec->hasCryptoOrDescrambler()) { memory = context->toHidlMemory(); + // TODO: copy if memory is null + offset += context->mHidlMemoryOffset; } else { buffer = context->toC2Buffer(offset, size); + // TODO: copy if buffer is null } } env->MonitorExit(lock.get()); @@ -2141,13 +2156,19 @@ static void android_media_MediaCodec_native_queueLinearBlock( } AString errorDetailMsg; - if (cryptoInfoObj != nullptr) { + if (codec->hasCryptoOrDescrambler()) { if (!memory) { + ALOGI("queueLinearBlock: no ashmem memory for encrypted content"); throwExceptionAsNecessary(env, BAD_VALUE); return; } - - NativeCryptoInfo cryptoInfo{env, cryptoInfoObj}; + NativeCryptoInfo cryptoInfo = [env, cryptoInfoObj, size]{ + if (cryptoInfoObj == nullptr) { + return NativeCryptoInfo{size}; + } else { + return NativeCryptoInfo{env, cryptoInfoObj}; + } + }(); err = codec->queueEncryptedLinearBlock( index, memory, @@ -2162,6 +2183,7 @@ static void android_media_MediaCodec_native_queueLinearBlock( &errorDetailMsg); } else { if (!buffer) { + ALOGI("queueLinearBlock: no C2Buffer found"); throwExceptionAsNecessary(env, BAD_VALUE); return; } @@ -2955,13 +2977,13 @@ static jobject android_media_MediaCodec_LinearBlock_native_map( context->mLegacyBuffer->size(), true, // readOnly true /* clearBuffer */); - } else if (context->mHeap) { + } else if (context->mMemory) { return CreateByteBuffer( env, - static_cast<uint8_t *>(context->mHeap->getBase()) + context->mHeap->getOffset(), - context->mHeap->getSize(), + context->mMemory->unsecurePointer(), + context->mMemory->size(), 0, - context->mHeap->getSize(), + context->mMemory->size(), false, // readOnly true /* clearBuffer */); } @@ -3011,8 +3033,26 @@ static void android_media_MediaCodec_LinearBlock_native_obtain( } } if (hasSecure && !hasNonSecure) { - context->mHeap = new MemoryHeapBase(capacity); - context->mMemory = hardware::fromHeap(context->mHeap); + constexpr size_t kInitialDealerCapacity = 1048576; // 1MB + thread_local sp<MemoryDealer> sDealer = new MemoryDealer( + kInitialDealerCapacity, "JNI(1MB)"); + context->mMemory = sDealer->allocate(capacity); + if (context->mMemory == nullptr) { + size_t newDealerCapacity = sDealer->getMemoryHeap()->getSize() * 2; + while (capacity * 2 > newDealerCapacity) { + newDealerCapacity *= 2; + } + ALOGI("LinearBlock.native_obtain: " + "Dealer capacity increasing from %zuMB to %zuMB", + sDealer->getMemoryHeap()->getSize() / 1048576, + newDealerCapacity / 1048576); + sDealer = new MemoryDealer( + newDealerCapacity, + AStringPrintf("JNI(%zuMB)", newDealerCapacity).c_str()); + context->mMemory = sDealer->allocate(capacity); + } + context->mHidlMemory = hardware::fromHeap(context->mMemory->getMemory( + &context->mHidlMemoryOffset, &context->mHidlMemorySize)); } else { context->mBlock = MediaCodec::FetchLinearBlock(capacity, names); if (!context->mBlock) { diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index 400ce1bc98e7..5c34341a86a1 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -162,6 +162,8 @@ struct JMediaCodec : public AHandler { void selectAudioPresentation(const int32_t presentationId, const int32_t programId); + bool hasCryptoOrDescrambler() { return mHasCryptoOrDescrambler; } + protected: virtual ~JMediaCodec(); @@ -181,6 +183,7 @@ private: sp<MediaCodec> mCodec; AString mNameAtCreation; bool mGraphicOutput{false}; + bool mHasCryptoOrDescrambler{false}; std::once_flag mReleaseFlag; sp<AMessage> mCallbackNotification; diff --git a/media/jni/android_media_MediaCodecLinearBlock.h b/media/jni/android_media_MediaCodecLinearBlock.h index 08438340d803..8f1d2fa35d70 100644 --- a/media/jni/android_media_MediaCodecLinearBlock.h +++ b/media/jni/android_media_MediaCodecLinearBlock.h @@ -31,8 +31,10 @@ struct JMediaCodecLinearBlock { std::shared_ptr<C2LinearBlock> mBlock; std::shared_ptr<C2WriteView> mReadWriteMapping; - sp<IMemoryHeap> mHeap; - sp<hardware::HidlMemory> mMemory; + sp<IMemory> mMemory; + sp<hardware::HidlMemory> mHidlMemory; + ssize_t mHidlMemoryOffset; + size_t mHidlMemorySize; sp<MediaCodecBuffer> mLegacyBuffer; @@ -56,8 +58,8 @@ struct JMediaCodecLinearBlock { } sp<hardware::HidlMemory> toHidlMemory() { - if (mMemory) { - return mMemory; + if (mHidlMemory) { + return mHidlMemory; } return nullptr; } @@ -65,4 +67,4 @@ struct JMediaCodecLinearBlock { } // namespace android -#endif // _ANDROID_MEDIA_MEDIACODECLINEARBLOCK_H_
\ No newline at end of file +#endif // _ANDROID_MEDIA_MEDIACODECLINEARBLOCK_H_ diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp index 0c1e9a2ad7bd..262ec76da26e 100644 --- a/media/jni/android_media_MediaMuxer.cpp +++ b/media/jni/android_media_MediaMuxer.cpp @@ -26,15 +26,11 @@ #include <unistd.h> #include <fcntl.h> -#include <android/api-level.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> -#include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaMuxer.h> -extern "C" int android_get_application_target_sdk_version(); - namespace android { struct fields_t { @@ -233,31 +229,11 @@ static void android_media_MediaMuxer_stop(JNIEnv *env, jclass /* clazz */, status_t err = muxer->stop(); - if (android_get_application_target_sdk_version() >= __ANDROID_API_R__) { - switch (err) { - case OK: - break; - case ERROR_IO: { - jniThrowException(env, "java/lang/UncheckedIOException", - "Muxer stopped unexpectedly"); - return; - } - case ERROR_MALFORMED: { - jniThrowException(env, "java/io/IOError", - "Failure of reading or writing operation"); - return; - } - default: { - jniThrowException(env, "java/lang/IllegalStateException", - "Failed to stop the muxer"); - return; - } - } - } else { - if (err != OK) { - jniThrowException(env, "java/lang/IllegalStateException", "Failed to stop the muxer"); - return; - } + if (err != OK) { + ALOGE("Error during stop:%d", err); + jniThrowException(env, "java/lang/IllegalStateException", + "Error during stop(), muxer would have stopped already"); + return; } } diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index e42e43801936..3dbf5a4d2b6b 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1376,4 +1376,7 @@ <!-- A content description for work profile app [CHAR LIMIT=35] --> <string name="accessibility_work_profile_app_description">Work <xliff:g id="app_name" example="Camera">%s</xliff:g></string> + + <!-- Name of the 3.5mm and usb audio device. [CHAR LIMIT=40] --> + <string name="media_transfer_wired_usb_device_name">Wired headphone</string> </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java index 8ea5ff1bf662..9a3ae1be14c0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java @@ -61,11 +61,11 @@ public class PhoneMediaDevice extends MediaDevice { switch (mRouteInfo.getType()) { case TYPE_WIRED_HEADSET: case TYPE_WIRED_HEADPHONES: - name = mContext.getString(R.string.media_transfer_wired_device_name); - break; case TYPE_USB_DEVICE: case TYPE_USB_HEADSET: case TYPE_USB_ACCESSORY: + name = mContext.getString(R.string.media_transfer_wired_usb_device_name); + break; case TYPE_DOCK: case TYPE_HDMI: name = mRouteInfo.getName(); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java index 47f6fe3bce02..421e5c0db1a1 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java @@ -100,12 +100,12 @@ public class PhoneMediaDeviceTest { when(mInfo.getName()).thenReturn(deviceName); assertThat(mPhoneMediaDevice.getName()) - .isEqualTo(mContext.getString(R.string.media_transfer_wired_device_name)); + .isEqualTo(mContext.getString(R.string.media_transfer_wired_usb_device_name)); when(mInfo.getType()).thenReturn(TYPE_USB_DEVICE); assertThat(mPhoneMediaDevice.getName()) - .isEqualTo(deviceName); + .isEqualTo(mContext.getString(R.string.media_transfer_wired_usb_device_name)); when(mInfo.getType()).thenReturn(TYPE_BUILTIN_SPEAKER); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 83d72189de74..b85c7714bf96 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -346,22 +346,6 @@ android:excludeFromRecents="true" android:exported="false" /> - <!-- - The following is used as a no-op/null home activity when - no other MAIN/HOME activity is present (e.g., in CSI). - --> - <activity android:name=".NullHome" - android:excludeFromRecents="true" - android:label="" - android:screenOrientation="nosensor"> - <!-- The priority here is set to be lower than that for Settings --> - <intent-filter android:priority="-1100"> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.HOME" /> - <category android:name="android.intent.category.DEFAULT" /> - </intent-filter> - </activity> - <receiver android:name=".BugreportRequestedReceiver" android:permission="android.permission.TRIGGER_SHELL_BUGREPORT"> diff --git a/packages/Shell/res/layout/null_home_finishing_boot.xml b/packages/Shell/res/layout/null_home_finishing_boot.xml deleted file mode 100644 index 5f9563a5d25c..000000000000 --- a/packages/Shell/res/layout/null_home_finishing_boot.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2020 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="#80000000" - android:forceHasOverlappingRendering="false"> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_gravity="center" - android:layout_marginStart="16dp" - android:layout_marginEnd="16dp"> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="40sp" - android:textColor="?android:attr/textColorPrimary" - android:text="@*android:string/android_start_title"/> - <ProgressBar - style="@android:style/Widget.Material.ProgressBar.Horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="12.75dp" - android:colorControlActivated="?android:attr/textColorPrimary" - android:indeterminate="true"/> - </LinearLayout> -</FrameLayout> diff --git a/packages/Shell/src/com/android/shell/NullHome.java b/packages/Shell/src/com/android/shell/NullHome.java deleted file mode 100644 index bd975614a50a..000000000000 --- a/packages/Shell/src/com/android/shell/NullHome.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.shell; - -import android.app.Activity; -import android.os.Bundle; -import android.util.Log; - -/** - * This covers the fallback case where no launcher is available. - * Usually Settings.apk has one fallback home activity. - * Settings.apk, however, is not part of CSI, which needs to be - * standalone (bootable and testable). - */ -public class NullHome extends Activity { - private static final String TAG = "NullHome"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Log.i(TAG, "onCreate"); - setContentView(R.layout.null_home_finishing_boot); - } - - protected void onDestroy() { - super.onDestroy(); - Log.i(TAG, "onDestroy"); - } -} diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 4510b87556c7..055b2beaaa65 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -155,6 +155,7 @@ <!-- Screen Recording --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> + <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"/> <!-- Assist --> <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" /> diff --git a/packages/SystemUI/res/anim/media_button_state_list_animator.xml b/packages/SystemUI/res/anim/media_button_state_list_animator.xml new file mode 100644 index 000000000000..62ebbaa112ea --- /dev/null +++ b/packages/SystemUI/res/anim/media_button_state_list_animator.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true"> + <set> + <objectAnimator + android:interpolator="@interpolator/control_state" + android:duration="50" + android:propertyName="scaleX" + android:valueTo="0.9" + android:valueType="floatType" /> + <objectAnimator + android:interpolator="@interpolator/control_state" + android:duration="50" + android:propertyName="scaleY" + android:valueTo="0.9" + android:valueType="floatType" /> + </set> + </item> + <item> + <set> + <objectAnimator + android:interpolator="@interpolator/control_state" + android:duration="250" + android:propertyName="scaleX" + android:valueTo="1" + android:valueType="floatType" /> + <objectAnimator + android:interpolator="@interpolator/control_state" + android:duration="250" + android:propertyName="scaleY" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </item> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml index 1b42ceb50bf7..99b9ced53090 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml @@ -14,9 +14,7 @@ limitations under the License. --> -<!-- RelativeLayouts have an issue enforcing minimum heights, so just - work around this for now with LinearLayouts. --> -<LinearLayout +<com.android.systemui.globalactions.GlobalActionsItem xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="0dp" android:layout_weight="1" @@ -42,7 +40,7 @@ android:id="@*android:id/message" android:layout_width="match_parent" android:layout_height="wrap_content" - android:ellipsize="marquee" + android:ellipsize="end" android:marqueeRepeatLimit="marquee_forever" android:maxLines="2" android:textSize="12sp" @@ -51,4 +49,4 @@ android:breakStrategy="high_quality" android:hyphenationFrequency="full" android:textAppearance="?android:attr/textAppearanceSmall" /> -</LinearLayout> +</com.android.systemui.globalactions.GlobalActionsItem> diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml index e4e9d2975220..ab4732de7124 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml @@ -46,8 +46,6 @@ <com.android.systemui.globalactions.MinHeightScrollView android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset" - android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset" android:orientation="vertical" android:scrollbars="none" > diff --git a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml index e4ae7c1f5827..46396e3e62b4 100644 --- a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml +++ b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml @@ -20,23 +20,29 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/screenshot_action_chip_margin_right" + android:paddingVertical="@dimen/screenshot_action_chip_margin_vertical" android:layout_gravity="center" - android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical" - android:background="@drawable/action_chip_background" - android:alpha="0.0" - android:gravity="center"> - <ImageView - android:id="@+id/screenshot_action_chip_icon" - android:layout_width="@dimen/screenshot_action_chip_icon_size" - android:layout_height="@dimen/screenshot_action_chip_icon_size" - android:layout_marginStart="@dimen/screenshot_action_chip_padding_start" - android:layout_marginEnd="@dimen/screenshot_action_chip_padding_middle"/> - <TextView - android:id="@+id/screenshot_action_chip_text" + android:gravity="center" + android:alpha="0.0"> + <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/screenshot_action_chip_padding_end" - android:fontFamily="@*android:string/config_headlineFontFamilyMedium" - android:textSize="@dimen/screenshot_action_chip_text_size" - android:textColor="@color/global_screenshot_button_text"/> + android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical" + android:background="@drawable/action_chip_background" + android:gravity="center"> + <ImageView + android:id="@+id/screenshot_action_chip_icon" + android:layout_width="@dimen/screenshot_action_chip_icon_size" + android:layout_height="@dimen/screenshot_action_chip_icon_size" + android:layout_marginStart="@dimen/screenshot_action_chip_padding_start" + android:layout_marginEnd="@dimen/screenshot_action_chip_padding_middle"/> + <TextView + android:id="@+id/screenshot_action_chip_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/screenshot_action_chip_padding_end" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textSize="@dimen/screenshot_action_chip_text_size" + android:textColor="@color/global_screenshot_button_text"/> + </LinearLayout> </com.android.systemui.screenshot.ScreenshotActionChip> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index a3d32c12d1c0..31edcf643285 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -314,13 +314,14 @@ <dimen name="screenshot_dismiss_button_margin">8dp</dimen> <dimen name="screenshot_action_container_offset_y">32dp</dimen> <dimen name="screenshot_action_container_corner_radius">10dp</dimen> - <dimen name="screenshot_action_container_padding_vertical">16dp</dimen> + <dimen name="screenshot_action_container_padding_vertical">6dp</dimen> <dimen name="screenshot_action_container_margin_horizontal">8dp</dimen> <dimen name="screenshot_action_container_padding_left">96dp</dimen> <dimen name="screenshot_action_container_padding_right">8dp</dimen> <!-- Radius of the chip background on global screenshot actions --> <dimen name="screenshot_button_corner_radius">20dp</dimen> <dimen name="screenshot_action_chip_margin_right">8dp</dimen> + <dimen name="screenshot_action_chip_margin_vertical">10dp</dimen> <dimen name="screenshot_action_chip_padding_vertical">7dp</dimen> <dimen name="screenshot_action_chip_icon_size">18dp</dimen> <dimen name="screenshot_action_chip_padding_start">8dp</dimen> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index b461295dc384..4f42370483c4 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -623,6 +623,7 @@ <style name="MediaPlayer.Button" parent="@android:style/Widget.Material.Button.Borderless.Small"> <item name="android:background">@null</item> <item name="android:tint">@android:color/white</item> + <item name="android:stateListAnimator">@anim/media_button_state_list_animator</item> </style> <!-- Used to style charging animation AVD animation --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl index 0350f2d47569..114472b15f02 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl @@ -16,6 +16,7 @@ package com.android.systemui.shared.recents; +import android.graphics.Rect; import android.graphics.Region; import android.os.Bundle; import android.view.MotionEvent; @@ -69,4 +70,9 @@ oneway interface IOverviewProxy { * Sent when some system ui state changes. */ void onSystemUiStateChanged(int stateFlags) = 16; + + /** + * Sent when the split screen is resized + */ + void onSplitScreenSecondaryBoundsChanged(in Rect bounds, in Rect insets) = 17; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 136935080824..5a78c903109c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -86,6 +86,8 @@ public class QuickStepContract { // enabled (since it's used to navigate back within the bubbled app, or to collapse the bubble // stack. public static final int SYSUI_STATE_BUBBLES_EXPANDED = 1 << 14; + // The global actions dialog is showing + public static final int SYSUI_STATE_GLOBAL_ACTIONS_SHOWING = 1 << 15; @Retention(RetentionPolicy.SOURCE) @IntDef({SYSUI_STATE_SCREEN_PINNING, @@ -102,7 +104,8 @@ public class QuickStepContract { SYSUI_STATE_SEARCH_DISABLED, SYSUI_STATE_TRACING_ENABLED, SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED, - SYSUI_STATE_BUBBLES_EXPANDED + SYSUI_STATE_BUBBLES_EXPANDED, + SYSUI_STATE_GLOBAL_ACTIONS_SHOWING }) public @interface SystemUiStateFlags {} @@ -119,6 +122,7 @@ public class QuickStepContract { str.add((flags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0 ? "keygrd_occluded" : ""); str.add((flags & SYSUI_STATE_BOUNCER_SHOWING) != 0 ? "bouncer_visible" : ""); + str.add((flags & SYSUI_STATE_GLOBAL_ACTIONS_SHOWING) != 0 ? "global_actions" : ""); str.add((flags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0 ? "a11y_click" : ""); str.add((flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0 ? "a11y_long_click" : ""); str.add((flags & SYSUI_STATE_TRACING_ENABLED) != 0 ? "tracing" : ""); @@ -192,8 +196,9 @@ public class QuickStepContract { * disabled. */ public static boolean isBackGestureDisabled(int sysuiStateFlags) { - // Always allow when the bouncer is showing (even on top of the keyguard) - if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0) { + // Always allow when the bouncer/global actions is showing (even on top of the keyguard) + if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0 + || (sysuiStateFlags & SYSUI_STATE_GLOBAL_ACTIONS_SHOWING) != 0) { return false; } // Disable when in immersive, or the notifications are interactive diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java index 8cd89ddabe72..525e98971266 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java @@ -22,6 +22,7 @@ import android.content.ComponentName; import android.content.Context; import android.os.Handler; import android.os.SystemClock; +import android.provider.Settings; import android.util.Log; import android.view.accessibility.AccessibilityManager; @@ -61,6 +62,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac private static final long DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS = 0; private static final long DEFAULT_SHOW_AND_GO_DURATION_MS = TimeUnit.SECONDS.toMillis(3); + private static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete"; /** * This is the default behavior that will be used once the system is up. It will be set once the @@ -203,6 +205,10 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac } private boolean handlesUnblocked(boolean ignoreThreshold) { + if (!isUserSetupComplete()) { + return false; + } + long timeSinceHidden = SystemClock.elapsedRealtime() - mHandlesLastHiddenAt; boolean notThrottled = ignoreThreshold || timeSinceHidden >= getShownFrequencyThreshold(); ComponentName assistantComponent = @@ -284,6 +290,11 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac mShowAndGoEndsAt = 0; } + private boolean isUserSetupComplete() { + return Settings.Secure.getInt( + mContext.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1; + } + @VisibleForTesting void setInGesturalModeForTest(boolean inGesturalMode) { mInGesturalMode = inGesturalMode; diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt index 9055479c47fe..10e913743224 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt @@ -58,7 +58,7 @@ class ControlActionCoordinatorImpl @Inject constructor( override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) { bouncerOrRun { - val effect = if (isChecked) Vibrations.toggleOnEffect else Vibrations.toggleOffEffect + val effect = if (!isChecked) Vibrations.toggleOnEffect else Vibrations.toggleOffEffect vibrate(effect) cvh.action(BooleanAction(templateId, !isChecked)) } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt index 236fa2d29aca..f97015282222 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt @@ -18,7 +18,6 @@ package com.android.systemui.controls.ui import android.app.ActivityView import android.app.Dialog -import android.content.ComponentName import android.content.Intent import android.provider.Settings import android.view.View @@ -58,17 +57,13 @@ class DetailDialog( launchIntent.putExtra(EXTRA_USE_PANEL, true) // Apply flags to make behaviour match documentLaunchMode=always. - launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) launchIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) view.startActivity(launchIntent) } override fun onActivityViewDestroyed(view: ActivityView) {} - - override fun onTaskCreated(taskId: Int, componentName: ComponentName) {} - - override fun onTaskRemovalStarted(taskId: Int) {} } init { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt index a97113cc598b..c0f6aabe2427 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt @@ -20,7 +20,7 @@ import android.os.VibrationEffect import android.os.VibrationEffect.Composition.PRIMITIVE_TICK object Vibrations { - private const val TOGGLE_TICK_COUNT = 12 + private const val TOGGLE_TICK_COUNT = 40 val toggleOnEffect = initToggleOnEffect() val toggleOffEffect = initToggleOffEffect() @@ -29,6 +29,7 @@ object Vibrations { private fun initToggleOnEffect(): VibrationEffect { val composition = VibrationEffect.startComposition() + composition.addPrimitive(PRIMITIVE_TICK, 0.05f, 200) var i = 0 while (i++ < TOGGLE_TICK_COUNT) { composition.addPrimitive(PRIMITIVE_TICK, 0.05f, 0) @@ -43,7 +44,7 @@ object Vibrations { composition.addPrimitive(PRIMITIVE_TICK, 0.05f, 100) var i = 0 while (i++ < TOGGLE_TICK_COUNT) { - composition?.addPrimitive(PRIMITIVE_TICK, 0.05f, 0) + composition.addPrimitive(PRIMITIVE_TICK, 0.05f, 0) } return composition.compose() } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index 5e367046bd2b..65729372363a 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -34,6 +34,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.wakelock.DelayedWakeLock; @@ -56,6 +57,7 @@ public class DozeFactory { private final ProximitySensor mProximitySensor; private final DelayedWakeLock.Builder mDelayedWakeLockBuilder; private final Handler mHandler; + private final DelayableExecutor mDelayableExecutor; private final BiometricUnlockController mBiometricUnlockController; private final BroadcastDispatcher mBroadcastDispatcher; private final DozeHost mDozeHost; @@ -68,6 +70,7 @@ public class DozeFactory { DockManager dockManager, @Nullable IWallpaperManager wallpaperManager, ProximitySensor proximitySensor, DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler, + DelayableExecutor delayableExecutor, BiometricUnlockController biometricUnlockController, BroadcastDispatcher broadcastDispatcher, DozeHost dozeHost) { mFalsingManager = falsingManager; @@ -83,6 +86,7 @@ public class DozeFactory { mProximitySensor = proximitySensor; mDelayedWakeLockBuilder = delayedWakeLockBuilder; mHandler = handler; + mDelayableExecutor = delayableExecutor; mBiometricUnlockController = biometricUnlockController; mBroadcastDispatcher = broadcastDispatcher; mDozeHost = dozeHost; @@ -107,8 +111,8 @@ public class DozeFactory { new DozePauser(mHandler, machine, mAlarmManager, mDozeParameters.getPolicy()), new DozeFalsingManagerAdapter(mFalsingManager), createDozeTriggers(dozeService, mAsyncSensorManager, mDozeHost, - mAlarmManager, config, mDozeParameters, mHandler, wakeLock, machine, - mDockManager, mDozeLog), + mAlarmManager, config, mDozeParameters, mDelayableExecutor, wakeLock, + machine, mDockManager, mDozeLog), createDozeUi(dozeService, mDozeHost, wakeLock, machine, mHandler, mAlarmManager, mDozeParameters, mDozeLog), new DozeScreenState(wrappedService, mHandler, mDozeHost, mDozeParameters, @@ -135,11 +139,11 @@ public class DozeFactory { private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager, DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config, - DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine, - DockManager dockManager, DozeLog dozeLog) { + DozeParameters params, DelayableExecutor delayableExecutor, WakeLock wakeLock, + DozeMachine machine, DockManager dockManager, DozeLog dozeLog) { boolean allowPulseTriggers = true; return new DozeTriggers(context, machine, host, alarmManager, config, params, - sensorManager, handler, wakeLock, allowPulseTriggers, dockManager, + sensorManager, delayableExecutor, wakeLock, allowPulseTriggers, dockManager, mProximitySensor, dozeLog, mBroadcastDispatcher); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index e1081cd5ef82..78f8f673cab9 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -101,7 +101,8 @@ public class DozeSensors { public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager, DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock, - Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog) { + Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog, + ProximitySensor proximitySensor) { mContext = context; mAlarmManager = alarmManager; mSensorManager = sensorManager; @@ -111,6 +112,7 @@ public class DozeSensors { mProxCallback = proxCallback; mResolver = mContext.getContentResolver(); mCallback = callback; + mProximitySensor = proximitySensor; boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT); mSensors = new TriggerSensor[] { @@ -173,7 +175,6 @@ public class DozeSensors { dozeLog), }; - mProximitySensor = new ProximitySensor(context.getResources(), sensorManager); setProxListening(false); // Don't immediately start listening when we register. mProximitySensor.register( proximityEvent -> { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 3510e07d2cea..6a5501445a4d 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -26,7 +26,6 @@ import android.content.IntentFilter; import android.content.res.Configuration; import android.hardware.display.AmbientDisplayConfiguration; import android.metrics.LogMaker; -import android.os.Handler; import android.os.SystemClock; import android.os.UserHandle; import android.text.format.Formatter; @@ -43,6 +42,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.Assert; +import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.wakelock.WakeLock; @@ -152,9 +152,9 @@ public class DozeTriggers implements DozeMachine.Part { public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost, AlarmManager alarmManager, AmbientDisplayConfiguration config, - DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler, - WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager, - ProximitySensor proximitySensor, + DozeParameters dozeParameters, AsyncSensorManager sensorManager, + DelayableExecutor delayableExecutor, WakeLock wakeLock, boolean allowPulseTriggers, + DockManager dockManager, ProximitySensor proximitySensor, DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher) { mContext = context; mMachine = machine; @@ -165,10 +165,10 @@ public class DozeTriggers implements DozeMachine.Part { mWakeLock = wakeLock; mAllowPulseTriggers = allowPulseTriggers; mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters, - config, wakeLock, this::onSensor, this::onProximityFar, dozeLog); + config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor); mUiModeManager = mContext.getSystemService(UiModeManager.class); mDockManager = dockManager; - mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler); + mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, delayableExecutor); mDozeLog = dozeLog; mBroadcastDispatcher = broadcastDispatcher; } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 62744673e76e..d69c3b01493f 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -23,6 +23,7 @@ import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -124,9 +125,11 @@ import com.android.systemui.controls.management.ControlsListingController; import com.android.systemui.controls.ui.ControlsUiController; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; +import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; @@ -200,6 +203,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final UiEventLogger mUiEventLogger; private final NotificationShadeDepthController mDepthController; private final BlurUtils mBlurUtils; + private final SysUiState mSysUiState; // Used for RingerModeTracker private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); @@ -301,7 +305,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, @Background Executor backgroundExecutor, ControlsListingController controlsListingController, ControlsController controlsController, UiEventLogger uiEventLogger, - RingerModeTracker ringerModeTracker, @Main Handler handler) { + RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) { mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme); mWindowManagerFuncs = windowManagerFuncs; mAudioManager = audioManager; @@ -330,6 +334,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mBlurUtils = blurUtils; mRingerModeTracker = ringerModeTracker; mControlsController = controlsController; + mSysUiState = sysUiState; mMainHandler = handler; // receive broadcasts @@ -638,7 +643,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, mOverflowAdapter, getWalletPanelViewController(), mDepthController, mSysuiColorExtractor, mStatusBarService, mNotificationShadeWindowController, - shouldShowControls() ? mControlsUiController : null, mBlurUtils, + shouldShowControls() ? mControlsUiController : null, mBlurUtils, mSysUiState, shouldUseControlsLayout(), this::onRotate, mKeyguardShowing); dialog.setCanceledOnTouchOutside(false); // Handled by the custom class. dialog.setOnDismissListener(this); @@ -1920,6 +1925,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final NotificationShadeWindowController mNotificationShadeWindowController; private final NotificationShadeDepthController mDepthController; private final BlurUtils mBlurUtils; + private final SysUiState mSysUiState; private final boolean mUseControlsLayout; private ListPopupWindow mOverflowPopup; private final Runnable mOnRotateCallback; @@ -1934,7 +1940,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, ControlsUiController controlsUiController, BlurUtils blurUtils, - boolean useControlsLayout, Runnable onRotateCallback, boolean keyguardShowing) { + SysUiState sysuiState, boolean useControlsLayout, Runnable onRotateCallback, + boolean keyguardShowing) { super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions); mContext = context; mAdapter = adapter; @@ -1945,6 +1952,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mNotificationShadeWindowController = notificationShadeWindowController; mControlsUiController = controlsUiController; mBlurUtils = blurUtils; + mSysUiState = sysuiState; mUseControlsLayout = useControlsLayout; mOnRotateCallback = onRotateCallback; mKeyguardShowing = keyguardShowing; @@ -2203,6 +2211,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mShowing = true; mHadTopUi = mNotificationShadeWindowController.getForceHasTopUi(); mNotificationShadeWindowController.setForceHasTopUi(true); + mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true) + .commitUpdate(mContext.getDisplayId()); ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView(); root.setOnApplyWindowInsetsListener((v, windowInsets) -> { @@ -2303,6 +2313,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, if (mControlsUiController != null) mControlsUiController.hide(); mNotificationShadeWindowController.setForceHasTopUi(mHadTopUi); mDepthController.updateGlobalDialogVisibility(0, null /* view */); + mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false) + .commitUpdate(mContext.getDisplayId()); super.dismiss(); } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java index c7612d41c45d..83046ef450c3 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java @@ -23,6 +23,7 @@ import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; import android.content.Context; import android.util.AttributeSet; import android.view.View; +import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.HardwareBgDrawable; @@ -78,6 +79,31 @@ public class GlobalActionsFlatLayout extends GlobalActionsLayout { } } + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + boolean anyTruncated = false; + ViewGroup listView = getListView(); + // Check to see if any of the GlobalActionsItems have had their messages truncated + for (int i = 0; i < listView.getChildCount(); i++) { + View child = listView.getChildAt(i); + if (child instanceof GlobalActionsItem) { + GlobalActionsItem item = (GlobalActionsItem) child; + anyTruncated = anyTruncated || item.isTruncated(); + } + } + // If any of the items have been truncated, set the all to single-line marquee + if (anyTruncated) { + for (int i = 0; i < listView.getChildCount(); i++) { + View child = listView.getChildAt(i); + if (child instanceof GlobalActionsItem) { + GlobalActionsItem item = (GlobalActionsItem) child; + item.setMarquee(true); + } + } + } + } + @VisibleForTesting protected float getGridItemSize() { return getContext().getResources().getDimension(R.dimen.global_actions_grid_item_height); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsItem.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsItem.java new file mode 100644 index 000000000000..07fa59200a1d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsItem.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.globalactions; + +import android.content.Context; +import android.text.Layout; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.internal.R; + +/** + * Layout for GlobalActions items. + */ +public class GlobalActionsItem extends LinearLayout { + public GlobalActionsItem(Context context) { + super(context); + } + + public GlobalActionsItem(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public GlobalActionsItem(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + private TextView getTextView() { + return (TextView) findViewById(R.id.message); + } + + /** + * Sets this item to marquee or not. + */ + public void setMarquee(boolean marquee) { + TextView text = getTextView(); + text.setSingleLine(marquee); + text.setEllipsize(marquee ? TextUtils.TruncateAt.MARQUEE : TextUtils.TruncateAt.END); + } + + /** + * Determines whether the message for this item has been truncated. + */ + public boolean isTruncated() { + TextView message = getTextView(); + if (message != null) { + Layout messageLayout = message.getLayout(); + if (messageLayout != null) { + if (messageLayout.getLineCount() > 0) { + // count the number of ellipses in the last line. + int ellipses = messageLayout.getEllipsisCount( + messageLayout.getLineCount() - 1); + // If ellipses are present, the line was forced to truncate. + return ellipses > 0; + } + } + } + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt index 524c6955ba4a..4ee4ad46d4c7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt @@ -67,5 +67,4 @@ class KeyguardMediaController @Inject constructor( statusBarStateController.state == StatusBarState.FULLSCREEN_USER_SWITCHER) view?.visibility = if (shouldBeVisible) View.VISIBLE else View.GONE } -} - +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 1691c53386d6..aee7a4649d08 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -228,7 +228,7 @@ public class MediaControlPanel { mLayoutAnimationHelper = new LayoutAnimationHelper(motionView); GoneChildrenHideHelper.clipGoneChildrenOnLayout(motionView); mKeyFrames = motionView.getDefinedTransitions().get(0).getKeyFrameList(); - mSeekBarObserver = new SeekBarObserver(motionView); + mSeekBarObserver = new SeekBarObserver(vh); mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver); SeekBar bar = vh.getSeekBar(); bar.setOnSeekBarChangeListener(mSeekBarViewModel.getSeekBarListener()); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 90c558a1ee97..f950d41f02b1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -65,7 +65,7 @@ class MediaDataManager @Inject constructor( private val context: Context, private val mediaControllerFactory: MediaControllerFactory, @Background private val backgroundExecutor: Executor, - @Main private val foregroundExcecutor: Executor + @Main private val foregroundExecutor: Executor ) { private val listeners: MutableSet<Listener> = mutableSetOf() @@ -201,7 +201,7 @@ class MediaDataManager @Inject constructor( } } - foregroundExcecutor.execute { + foregroundExecutor.execute { onMediaDataLoaded(key, MediaData(true, bgColor, app, smallIconDrawable, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent)) @@ -288,7 +288,7 @@ class MediaDataManager @Inject constructor( /** * Are there any media notifications active? */ - fun hasActiveMedia() = mediaEntries.size > 0 + fun hasActiveMedia() = mediaEntries.isNotEmpty() fun hasAnyMedia(): Boolean { // TODO: implement this when we implemented resumption diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt index 6e7b6bcb7502..240e44cb8db4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt @@ -26,8 +26,8 @@ class MediaHost @Inject constructor( /** * Get the current Media state. This also updates the location on screen */ - val currentState : MediaState - get () { + val currentState: MediaState + get() { hostView.getLocationOnScreen(tmpLocationOnScreen) var left = tmpLocationOnScreen[0] + hostView.paddingLeft var top = tmpLocationOnScreen[1] + hostView.paddingTop @@ -37,11 +37,11 @@ class MediaHost @Inject constructor( // the above could return negative widths, which is wrong if (right < left) { left = 0 - right = 0; + right = 0 } if (bottom < top) { bottom = 0 - top = 0; + top = 0 } state.boundsOnScreen.set(left, top, right, bottom) return state @@ -64,7 +64,7 @@ class MediaHost @Inject constructor( * transitions. */ fun init(@MediaLocation location: Int) { - this.location = location; + this.location = location hostView = mediaHierarchyManager.register(this) hostView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View?) { @@ -95,7 +95,7 @@ class MediaHost @Inject constructor( override var showsOnlyActiveMedia: Boolean = false override val boundsOnScreen: Rect = Rect() - override fun copy() : MediaState { + override fun copy(): MediaState { val mediaHostState = MediaHostState() mediaHostState.expansion = expansion mediaHostState.showsOnlyActiveMedia = showsOnlyActiveMedia @@ -104,7 +104,7 @@ class MediaHost @Inject constructor( return mediaHostState } - override fun interpolate(other: MediaState, amount: Float) : MediaState { + override fun interpolate(other: MediaState, amount: Float): MediaState { val result = MediaHostState() result.expansion = MathUtils.lerp(expansion, other.expansion, amount) val left = MathUtils.lerp(boundsOnScreen.left.toFloat(), @@ -121,10 +121,10 @@ class MediaHost @Inject constructor( if (other is MediaHostState) { result.measurementInput = other.measurementInput } - } else { + } else { result.measurementInput } - return result + return result } override fun getMeasuringInput(input: MeasurementInput): MediaMeasurementInput { @@ -138,8 +138,8 @@ interface MediaState { var expansion: Float var showsOnlyActiveMedia: Boolean val boundsOnScreen: Rect - fun copy() : MediaState - fun interpolate(other: MediaState, amount: Float) : MediaState + fun copy(): MediaState + fun interpolate(other: MediaState, amount: Float): MediaState fun getMeasuringInput(input: MeasurementInput): MediaMeasurementInput } /** @@ -147,7 +147,8 @@ interface MediaState { */ data class MediaMeasurementInput( private val viewInput: MeasurementInput, - val expansion: Float) : MeasurementInput by viewInput { + val expansion: Float +) : MeasurementInput by viewInput { override fun sameAs(input: MeasurementInput?): Boolean { if (!(input is MediaMeasurementInput)) { @@ -155,5 +156,4 @@ data class MediaMeasurementInput( } return width == input.width && expansion == input.expansion } -} - +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt index 110b08c4b808..cd8ed265bd53 100644 --- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt @@ -16,58 +16,41 @@ package com.android.systemui.media -import android.content.res.ColorStateList -import android.graphics.Color import android.text.format.DateUtils -import android.view.View -import android.widget.SeekBar -import android.widget.TextView import androidx.annotation.UiThread import androidx.lifecycle.Observer -import com.android.systemui.R - /** * Observer for changes from SeekBarViewModel. * * <p>Updates the seek bar views in response to changes to the model. */ -class SeekBarObserver(view: View) : Observer<SeekBarViewModel.Progress> { - - private val seekBarView: SeekBar - private val elapsedTimeView: TextView - private val totalTimeView: TextView - - init { - seekBarView = view.findViewById(R.id.media_progress_bar) - elapsedTimeView = view.findViewById(R.id.media_elapsed_time) - totalTimeView = view.findViewById(R.id.media_total_time) - } +class SeekBarObserver(private val holder: PlayerViewHolder) : Observer<SeekBarViewModel.Progress> { /** Updates seek bar views when the data model changes. */ @UiThread override fun onChanged(data: SeekBarViewModel.Progress) { if (!data.enabled) { - seekBarView.setEnabled(false) - seekBarView.getThumb().setAlpha(0) - seekBarView.setProgress(0) - elapsedTimeView.setText("") - totalTimeView.setText("") + holder.seekBar.setEnabled(false) + holder.seekBar.getThumb().setAlpha(0) + holder.seekBar.setProgress(0) + holder.elapsedTimeView.setText("") + holder.totalTimeView.setText("") return } - seekBarView.getThumb().setAlpha(if (data.seekAvailable) 255 else 0) - seekBarView.setEnabled(data.seekAvailable) + holder.seekBar.getThumb().setAlpha(if (data.seekAvailable) 255 else 0) + holder.seekBar.setEnabled(data.seekAvailable) data.elapsedTime?.let { - seekBarView.setProgress(it) - elapsedTimeView.setText(DateUtils.formatElapsedTime( + holder.seekBar.setProgress(it) + holder.elapsedTimeView.setText(DateUtils.formatElapsedTime( it / DateUtils.SECOND_IN_MILLIS)) } data.duration?.let { - seekBarView.setMax(it) - totalTimeView.setText(DateUtils.formatElapsedTime( + holder.seekBar.setMax(it) + holder.totalTimeView.setText(DateUtils.formatElapsedTime( it / DateUtils.SECOND_IN_MILLIS)) } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java index 13516a9e03c4..7f7e1085d497 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java @@ -170,9 +170,9 @@ public class PipAnimationController { private final @AnimationType int mAnimationType; private final Rect mDestinationBounds = new Rect(); - private T mStartValue; + protected T mCurrentValue; + protected T mStartValue; private T mEndValue; - private T mCurrentValue; private PipAnimationCallback mPipAnimationCallback; private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; @@ -288,7 +288,6 @@ public class PipAnimationController { */ void updateEndValue(T endValue) { mEndValue = endValue; - mStartValue = mCurrentValue; } SurfaceControl.Transaction newSurfaceControlTransaction() { @@ -337,6 +336,12 @@ public class PipAnimationController { tx.show(leash); tx.apply(); } + + @Override + void updateEndValue(Float endValue) { + super.updateEndValue(endValue); + mStartValue = mCurrentValue; + } }; } @@ -392,6 +397,14 @@ public class PipAnimationController { getSurfaceTransactionHelper().resetScale(tx, leash, getDestinationBounds()) .crop(tx, leash, getDestinationBounds()); } + + @Override + void updateEndValue(Rect endValue) { + super.updateEndValue(endValue); + if (mStartValue != null && mCurrentValue != null) { + mStartValue.set(mCurrentValue); + } + } }; } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java index 394f9975579d..93605170f22e 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java @@ -61,12 +61,6 @@ public class PipBoundsHandler { private final DisplayInfo mDisplayInfo = new DisplayInfo(); private final Rect mTmpInsets = new Rect(); - /** - * Tracks the destination bounds, used for any following - * {@link #onMovementBoundsChanged(Rect, Rect, Rect, DisplayInfo)} calculations. - */ - private final Rect mLastDestinationBounds = new Rect(); - private ComponentName mLastPipComponentName; private float mReentrySnapFraction = INVALID_SNAP_FRACTION; private Size mReentrySize; @@ -198,11 +192,6 @@ public class PipBoundsHandler { mReentrySnapFraction = INVALID_SNAP_FRACTION; mReentrySize = null; mLastPipComponentName = null; - mLastDestinationBounds.setEmpty(); - } - - public Rect getLastDestinationBounds() { - return mLastDestinationBounds; } public Rect getDisplayBounds() { @@ -262,7 +251,6 @@ public class PipBoundsHandler { false /* useCurrentMinEdgeSize */); } mAspectRatio = aspectRatio; - mLastDestinationBounds.set(destinationBounds); return destinationBounds; } @@ -276,8 +264,8 @@ public class PipBoundsHandler { * * @return {@code true} if internal {@link DisplayInfo} is rotated, {@code false} otherwise. */ - public boolean onDisplayRotationChanged(Rect outBounds, int displayId, int fromRotation, - int toRotation, WindowContainerTransaction t) { + public boolean onDisplayRotationChanged(Rect outBounds, Rect oldBounds, int displayId, + int fromRotation, int toRotation, WindowContainerTransaction t) { // Bail early if the event is not sent to current {@link #mDisplayInfo} if ((displayId != mDisplayInfo.displayId) || (fromRotation == toRotation)) { return false; @@ -295,7 +283,7 @@ public class PipBoundsHandler { } // Calculate the snap fraction of the current stack along the old movement bounds - final Rect postChangeStackBounds = new Rect(mLastDestinationBounds); + final Rect postChangeStackBounds = new Rect(oldBounds); final float snapFraction = getSnapFraction(postChangeStackBounds); // Populate the new {@link #mDisplayInfo}. @@ -313,7 +301,6 @@ public class PipBoundsHandler { snapFraction); outBounds.set(postChangeStackBounds); - mLastDestinationBounds.set(outBounds); t.setBounds(pinnedStackInfo.stackToken, outBounds); return true; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 78d2d9857628..ae6100675cb4 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -600,6 +600,7 @@ public class PipTaskOrganizer extends TaskOrganizer { Log.w(TAG, "Abort animation, invalid leash"); return; } + mLastReportedBounds.set(destinationBounds); final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper .crop(tx, mLeash, destinationBounds) diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 30d6cd9d23b7..64df2ffee1c6 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -95,7 +95,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio private final DisplayChangeController.OnDisplayChangingListener mRotationController = ( int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> { final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mTmpNormalBounds, - displayId, fromRotation, toRotation, t); + mPipTaskOrganizer.getLastReportedBounds(), displayId, fromRotation, toRotation, t); if (changed) { updateMovementBounds(mTmpNormalBounds, true /* fromRotation */, false /* fromImeAdjustment */, false /* fromShelfAdjustment */); @@ -293,7 +293,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio final boolean changed = mPipBoundsHandler.setShelfHeight(visible, height); if (changed) { mTouchHandler.onShelfVisibilityChanged(visible, height); - updateMovementBounds(mPipBoundsHandler.getLastDestinationBounds(), + updateMovementBounds(mPipTaskOrganizer.getLastReportedBounds(), false /* fromRotation */, false /* fromImeAdjustment */, true /* fromShelfAdjustment */); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 59c962da7baa..af9dd574c8af 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -424,20 +424,28 @@ public class PipTouchHandler { // If this is from an IME or shelf adjustment, then we should move the PiP so that it is not // occluded by the IME or shelf. - if (fromImeAdjustment || fromShelfAdjustment || fromDisplayRotationChanged) { + if (fromImeAdjustment || fromShelfAdjustment) { if (mTouchState.isUserInteracting()) { // Defer the update of the current movement bounds until after the user finishes // touching the screen } else { final float offsetBufferPx = BOTTOM_OFFSET_BUFFER_DP * mContext.getResources().getDisplayMetrics().density; - final Rect toMovementBounds = mMenuState == MENU_STATE_FULL && willResizeMenu() + final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu(); + final Rect toMovementBounds = isExpanded ? new Rect(expandedMovementBounds) : new Rect(normalMovementBounds); final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets; final int toBottom = toMovementBounds.bottom < toMovementBounds.top ? toMovementBounds.bottom : toMovementBounds.bottom - extraOffset; + + if (isExpanded) { + curBounds.set(mExpandedBounds); + mSnapAlgorithm.applySnapFraction(curBounds, toMovementBounds, + mSavedSnapFraction); + } + if ((Math.min(prevBottom, toBottom) - offsetBufferPx) <= curBounds.top && curBounds.top <= (Math.max(prevBottom, toBottom) + offsetBufferPx)) { mMotionHelper.animateToOffset(curBounds, toBottom - curBounds.top); diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 34a9e28b943a..b272b60f3593 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -842,7 +842,26 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis Log.e(TAG_OPS, "Failed to get overview proxy for assistant visibility."); } } catch (RemoteException e) { - Log.e(TAG_OPS, "Failed to call onAssistantVisibilityChanged()", e); + Log.e(TAG_OPS, "Failed to call notifyAssistantVisibilityChanged()", e); + } + } + + /** + * Notifies the Launcher of split screen size changes + * @param secondaryWindowBounds Bounds of the secondary window including the insets + * @param secondaryWindowInsets stable insets received by the secondary window + */ + public void notifySplitScreenBoundsChanged( + Rect secondaryWindowBounds, Rect secondaryWindowInsets) { + try { + if (mOverviewProxy != null) { + mOverviewProxy.onSplitScreenSecondaryBoundsChanged( + secondaryWindowBounds, secondaryWindowInsets); + } else { + Log.e(TAG_OPS, "Failed to get overview proxy for split screen bounds."); + } + } catch (RemoteException e) { + Log.e(TAG_OPS, "Failed to call onSplitScreenSecondaryBoundsChanged()", e); } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 839ea69953af..f2d2eb3a0836 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -642,13 +642,10 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset private void startAnimation(final Consumer<Uri> finisher, int w, int h, @Nullable Rect screenRect) { // If power save is on, show a toast so there is some visual indication that a - // screenshot - // has been taken. - PowerManager powerManager = (PowerManager) mContext.getSystemService( - Context.POWER_SERVICE); + // screenshot has been taken. + PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); if (powerManager.isPowerSaveMode()) { - Toast.makeText(mContext, R.string.screenshot_saved_title, - Toast.LENGTH_SHORT).show(); + Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show(); } mScreenshotAnimation = createScreenshotDropInAnimation(w, h, screenRect); @@ -706,24 +703,27 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mScreenshotPreview.buildLayer(); mScreenshotAnimation.start(); }); - }); - } private AnimatorSet createScreenshotDropInAnimation(int width, int height, Rect bounds) { + float screenWidth = mDisplayMetrics.widthPixels; + float screenHeight = mDisplayMetrics.heightPixels; + int rotation = mContext.getDisplay().getRotation(); float cornerScale; if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) { - cornerScale = (mCornerSizeX / (float) height); + cornerScale = (mCornerSizeX / screenHeight); } else { - cornerScale = (mCornerSizeX / (float) width); + cornerScale = (mCornerSizeX / screenWidth); } + float currentScale = width / screenWidth; - mScreenshotAnimatedView.setScaleX(1); - mScreenshotAnimatedView.setScaleY(1); - mScreenshotAnimatedView.setX(0); - mScreenshotAnimatedView.setY(0); + mScreenshotAnimatedView.setScaleX(currentScale); + mScreenshotAnimatedView.setScaleY(currentScale); + + mScreenshotAnimatedView.setPivotX(0); + mScreenshotAnimatedView.setPivotY(0); mScreenshotAnimatedView.setImageBitmap(mScreenBitmap); mScreenshotPreview.setImageBitmap(mScreenBitmap); @@ -744,12 +744,11 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset final PointF startPos = new PointF(bounds.centerX(), bounds.centerY()); float finalX; if (mDirectionLTR) { - finalX = mScreenshotOffsetXPx + width * cornerScale / 2f; + finalX = mScreenshotOffsetXPx + screenWidth * cornerScale / 2f; } else { - finalX = width - mScreenshotOffsetXPx - width * cornerScale / 2f; + finalX = screenWidth - mScreenshotOffsetXPx - screenWidth * cornerScale / 2f; } - float finalY = - mDisplayMetrics.heightPixels - mScreenshotOffsetYPx - height * cornerScale / 2f; + float finalY = screenHeight - mScreenshotOffsetYPx - screenHeight * cornerScale / 2f; final PointF finalPos = new PointF(finalX, finalY); ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1); @@ -757,13 +756,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset float xPositionPct = SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS; float scalePct = - SCREENSHOT_TO_CORNER_SCALE_DURATION_MS - / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS; + SCREENSHOT_TO_CORNER_SCALE_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS; toCorner.addUpdateListener(animation -> { float t = animation.getAnimatedFraction(); if (t < scalePct) { float scale = MathUtils.lerp( - 1, cornerScale, mFastOutSlowIn.getInterpolation(t / scalePct)); + currentScale, cornerScale, mFastOutSlowIn.getInterpolation(t / scalePct)); mScreenshotAnimatedView.setScaleX(scale); mScreenshotAnimatedView.setScaleY(scale); } else { @@ -777,13 +775,13 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset if (t < xPositionPct) { float xCenter = MathUtils.lerp(startPos.x, finalPos.x, mFastOutSlowIn.getInterpolation(t / xPositionPct)); - mScreenshotAnimatedView.setX(xCenter - width * currentScaleX / 2f); + mScreenshotAnimatedView.setX(xCenter - screenWidth * currentScaleX / 2f); } else { - mScreenshotAnimatedView.setX(finalPos.x - width * currentScaleX / 2f); + mScreenshotAnimatedView.setX(finalPos.x - screenWidth * currentScaleX / 2f); } float yCenter = MathUtils.lerp(startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t)); - mScreenshotAnimatedView.setY(yCenter - height * currentScaleY / 2f); + mScreenshotAnimatedView.setY(yCenter - screenHeight * currentScaleY / 2f); }); toCorner.addListener(new AnimatorListenerAdapter() { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java index 44b20e535cce..b5209bbbdd21 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java @@ -22,8 +22,8 @@ import android.content.Context; import android.graphics.drawable.Icon; import android.util.AttributeSet; import android.util.Log; +import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.TextView; import com.android.systemui.R; @@ -31,7 +31,7 @@ import com.android.systemui.R; /** * View for a chip with an icon and text. */ -public class ScreenshotActionChip extends LinearLayout { +public class ScreenshotActionChip extends FrameLayout { private static final String TAG = "ScreenshotActionChip"; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index db89cea385b7..8ca50cdddf71 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -19,7 +19,6 @@ package com.android.systemui.stackdivider; import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW; import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW; import static android.view.WindowManager.DOCKED_RIGHT; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import android.animation.AnimationHandler; import android.animation.Animator; @@ -39,7 +38,6 @@ import android.os.RemoteException; import android.util.AttributeSet; import android.util.Slog; import android.view.Display; -import android.view.InsetsState; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.SurfaceControl; @@ -48,10 +46,8 @@ import android.view.VelocityTracker; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewConfiguration; -import android.view.ViewRootImpl; import android.view.ViewTreeObserver.InternalInsetsInfo; import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; -import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; @@ -65,9 +61,10 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; import com.android.internal.policy.DockedDividerUtils; +import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.shared.system.WindowManagerWrapper; +import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.statusbar.FlingAnimationUtils; import java.util.function.Consumer; @@ -120,7 +117,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, private int mStartY; private int mStartPosition; private int mDockSide; - private final int[] mTempInt2 = new int[2]; private boolean mMoving; private int mTouchSlop; private boolean mBackgroundLifted; @@ -148,7 +144,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, private FlingAnimationUtils mFlingAnimationUtils; private SplitDisplayLayout mSplitLayout; private DividerCallbacks mCallback; - private final Rect mStableInsets = new Rect(); private final AnimationHandler mAnimationHandler = new AnimationHandler(); private boolean mGrowRecents; @@ -336,29 +331,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, } @Override - public WindowInsets onApplyWindowInsets(WindowInsets insets) { - if (isAttachedToWindow() - && ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL) { - // Our window doesn't cover entire display, so we use the display frame to re-calculate - // the insets. - final InsetsState state = getWindowInsetsController().getState(); - insets = state.calculateInsets(state.getDisplayFrame(), - null /* ignoringVisibilityState */, insets.isRound(), - insets.shouldAlwaysConsumeSystemBars(), insets.getDisplayCutout(), - 0 /* legacySystemUiFlags */, - SOFT_INPUT_ADJUST_NOTHING, null /* typeSideMap */); - } - if (mStableInsets.left != insets.getStableInsetLeft() - || mStableInsets.top != insets.getStableInsetTop() - || mStableInsets.right != insets.getStableInsetRight() - || mStableInsets.bottom != insets.getStableInsetBottom()) { - mStableInsets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(), - insets.getStableInsetRight(), insets.getStableInsetBottom()); - } - return super.onApplyWindowInsets(insets); - } - - @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (mFirstLayout) { @@ -381,6 +353,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, if (changed) { mWindowManagerProxy.setTouchRegion(new Rect(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), mHandle.getBottom())); + notifySplitScreenBoundsChanged(); } } @@ -405,19 +378,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, } public Rect getNonMinimizedSplitScreenSecondaryBounds() { - calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, - DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect); - mOtherTaskRect.bottom -= mStableInsets.bottom; - switch (mDockSide) { - case WindowManager.DOCKED_LEFT: - mOtherTaskRect.top += mStableInsets.top; - mOtherTaskRect.right -= mStableInsets.right; - break; - case WindowManager.DOCKED_RIGHT: - mOtherTaskRect.top += mStableInsets.top; - mOtherTaskRect.left += mStableInsets.left; - break; - } + mOtherTaskRect.set(mSplitLayout.mSecondary); return mOtherTaskRect; } @@ -681,6 +642,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, saveSnapTargetBeforeMinimized(saveTarget); } } + notifySplitScreenBoundsChanged(); }; anim.addListener(new AnimatorListenerAdapter() { @@ -713,6 +675,25 @@ public class DividerView extends FrameLayout implements OnTouchListener, return anim; } + private void notifySplitScreenBoundsChanged() { + mOtherTaskRect.set(mSplitLayout.mSecondary); + + mTmpRect.set(mSplitLayout.mDisplayLayout.stableInsets()); + switch (mSplitLayout.getPrimarySplitSide()) { + case WindowManager.DOCKED_LEFT: + mTmpRect.left = 0; + break; + case WindowManager.DOCKED_RIGHT: + mTmpRect.right = 0; + break; + case WindowManager.DOCKED_TOP: + mTmpRect.top = 0; + break; + } + Dependency.get(OverviewProxyService.class) + .notifySplitScreenBoundsChanged(mOtherTaskRect, mTmpRect); + } + private void cancelFlingAnimation() { if (mCurrentAnimator != null) { mCurrentAnimator.cancel(); @@ -846,8 +827,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, mDockedStackMinimized = minimized; if (mSplitLayout.mDisplayLayout.rotation() != mDefaultDisplay.getRotation()) { // Splitscreen to minimize is about to starts after rotating landscape to seascape, - // update insets, display info and snap algorithm targets - WindowManagerWrapper.getInstance().getStableInsets(mStableInsets); + // update display info and snap algorithm targets repositionSnapTargetBeforeMinimized(); } if (mIsInMinimizeInteraction != minimized || mCurrentAnimator != null) { @@ -1149,7 +1129,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, // Move a right-docked-app to line up with the divider while dragging it if (mDockSide == DOCKED_RIGHT) { - mDockedTaskRect.offset(Math.max(position, mStableInsets.left - mDividerSize) + mDockedTaskRect.offset(Math.max(position, -mDividerSize) - mDockedTaskRect.left + mDividerSize, 0); } resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, @@ -1164,7 +1144,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, // Move a docked app if from the right in position with the divider up to insets if (mDockSide == DOCKED_RIGHT) { - mDockedTaskRect.offset(Math.max(position, mStableInsets.left - mDividerSize) + mDockedTaskRect.offset(Math.max(position, -mDividerSize) - mDockedTaskRect.left + mDividerSize, 0); } calculateBoundsForPosition(taskPosition, DockedDividerUtils.invertDockSide(mDockSide), @@ -1180,7 +1160,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, // Move a right-docked-app to line up with the divider while dragging it if (mDockSide == DOCKED_RIGHT) { - mDockedTaskRect.offset(position - mStableInsets.left + mDividerSize, 0); + mDockedTaskRect.offset(position + mDividerSize, 0); } resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect); } else if (taskPosition != TASK_POSITION_SAME) { @@ -1238,34 +1218,10 @@ public class DividerView extends FrameLayout implements OnTouchListener, float fraction = getSnapAlgorithm().calculateDismissingFraction(position); fraction = Math.max(0, Math.min(fraction, 1f)); fraction = DIM_INTERPOLATOR.getInterpolation(fraction); - if (hasInsetsAtDismissTarget(dismissTarget)) { - - // Less darkening with system insets. - fraction *= 0.8f; - } return fraction; } /** - * @return true if and only if there are system insets at the location of the dismiss target - */ - private boolean hasInsetsAtDismissTarget(SnapTarget dismissTarget) { - if (isHorizontalDivision()) { - if (dismissTarget == getSnapAlgorithm().getDismissStartTarget()) { - return mStableInsets.top != 0; - } else { - return mStableInsets.bottom != 0; - } - } else { - if (dismissTarget == getSnapAlgorithm().getDismissStartTarget()) { - return mStableInsets.left != 0; - } else { - return mStableInsets.right != 0; - } - } - } - - /** * When the snap target is dismissing one side, make sure that the dismissing side doesn't get * 0 size. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 9f4932e74eaa..0fa1f553c1f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; +import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON; import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_ARTWORK; import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER; import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK; @@ -44,8 +45,6 @@ import android.util.Log; import android.view.View; import android.widget.ImageView; -import androidx.annotation.NonNull; - import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dependency; @@ -53,7 +52,6 @@ import com.android.systemui.Dumpable; import com.android.systemui.Interpolators; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.media.MediaData; import com.android.systemui.media.MediaDataManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.dagger.StatusBarModule; @@ -70,6 +68,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.Utils; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -79,7 +78,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; import dagger.Lazy; @@ -90,6 +89,7 @@ import dagger.Lazy; public class NotificationMediaManager implements Dumpable { private static final String TAG = "NotificationMediaManager"; public static final boolean DEBUG_MEDIA = false; + private static final long PAUSED_MEDIA_TIMEOUT = TimeUnit.MINUTES.toMillis(10); private final StatusBarStateController mStatusBarStateController = Dependency.get(StatusBarStateController.class); @@ -106,6 +106,7 @@ public class NotificationMediaManager implements Dumpable { } private final NotificationEntryManager mEntryManager; + private final MediaDataManager mMediaDataManager; @Nullable private Lazy<NotificationShadeWindowController> mNotificationShadeWindowController; @@ -117,7 +118,7 @@ public class NotificationMediaManager implements Dumpable { @Nullable private LockscreenWallpaper mLockscreenWallpaper; - private final Executor mMainExecutor; + private final DelayableExecutor mMainExecutor; private final Context mContext; private final MediaSessionManager mMediaSessionManager; @@ -130,6 +131,7 @@ public class NotificationMediaManager implements Dumpable { private MediaController mMediaController; private String mMediaNotificationKey; private MediaMetadata mMediaMetadata; + private Runnable mMediaTimeoutCancellation; private BackDropView mBackdrop; private ImageView mBackdropFront; @@ -159,11 +161,36 @@ public class NotificationMediaManager implements Dumpable { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state); } + if (mMediaTimeoutCancellation != null) { + mMediaTimeoutCancellation.run(); + mMediaTimeoutCancellation = null; + } if (state != null) { if (!isPlaybackActive(state.getState())) { clearCurrentMediaNotification(); } findAndUpdateMediaNotifications(); + scheduleMediaTimeout(state); + } + } + + private void scheduleMediaTimeout(PlaybackState state) { + final NotificationEntry entry; + synchronized (mEntryManager) { + entry = mEntryManager.getActiveNotificationUnfiltered(mMediaNotificationKey); + } + if (entry != null) { + if (!isPlayingState(state.getState())) { + mMediaTimeoutCancellation = mMainExecutor.executeDelayed(() -> { + synchronized (mEntryManager) { + if (mMediaNotificationKey == null) { + return; + } + mEntryManager.removeNotification(mMediaNotificationKey, null, + UNDEFINED_DISMISS_REASON); + } + }, PAUSED_MEDIA_TIMEOUT); + } } } @@ -189,7 +216,7 @@ public class NotificationMediaManager implements Dumpable { NotificationEntryManager notificationEntryManager, MediaArtworkProcessor mediaArtworkProcessor, KeyguardBypassController keyguardBypassController, - @Main Executor mainExecutor, + @Main DelayableExecutor mainExecutor, DeviceConfigProxy deviceConfig, MediaDataManager mediaDataManager) { mContext = context; @@ -205,6 +232,7 @@ public class NotificationMediaManager implements Dumpable { mNotificationShadeWindowController = notificationShadeWindowController; mEntryManager = notificationEntryManager; mMainExecutor = mainExecutor; + mMediaDataManager = mediaDataManager; notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java index b08eb9fafe41..8c9ce500e3ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -47,6 +47,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.util.DeviceConfigProxy; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.util.concurrent.Executor; @@ -98,7 +99,7 @@ public interface StatusBarDependenciesModule { NotificationEntryManager notificationEntryManager, MediaArtworkProcessor mediaArtworkProcessor, KeyguardBypassController keyguardBypassController, - @Main Executor mainExecutor, + @Main DelayableExecutor mainExecutor, DeviceConfigProxy deviceConfigProxy, MediaDataManager mediaDataManager) { return new NotificationMediaManager( 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 2be1531850c5..d6471243e053 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -151,16 +151,6 @@ public class NotificationEntryManager implements @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("NotificationEntryManager state:"); - pw.println(" mAllNotifications="); - if (mAllNotifications.size() == 0) { - pw.println("null"); - } else { - int i = 0; - for (NotificationEntry entry : mAllNotifications) { - dumpEntry(pw, " ", i, entry); - i++; - } - } pw.print(" mPendingNotifications="); if (mPendingNotifications.size() == 0) { pw.println("null"); @@ -360,8 +350,8 @@ public class NotificationEntryManager implements private final NotificationHandler mNotifListener = new NotificationHandler() { @Override public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { - final boolean isUpdateToInflatedNotif = mActiveNotifications.containsKey(sbn.getKey()); - if (isUpdateToInflatedNotif) { + final boolean isUpdate = mActiveNotifications.containsKey(sbn.getKey()); + if (isUpdate) { updateNotification(sbn, rankingMap); } else { addNotification(sbn, rankingMap); @@ -452,12 +442,16 @@ public class NotificationEntryManager implements } if (!lifetimeExtended) { // At this point, we are guaranteed the notification will be removed - abortExistingInflation(key, "removeNotification"); mAllNotifications.remove(pendingEntry); - mLeakDetector.trackGarbage(pendingEntry); } } - } else { + } + + if (!lifetimeExtended) { + abortExistingInflation(key, "removeNotification"); + } + + if (entry != null) { // If a manager needs to keep the notification around for whatever reason, we // keep the notification boolean entryDismissed = entry.isRowDismissed(); @@ -475,8 +469,6 @@ public class NotificationEntryManager implements if (!lifetimeExtended) { // At this point, we are guaranteed the notification will be removed - abortExistingInflation(key, "removeNotification"); - mAllNotifications.remove(entry); // Ensure any managers keeping the lifetime extended stop managing the entry cancelLifetimeExtension(entry); @@ -485,10 +477,13 @@ public class NotificationEntryManager implements entry.removeRow(); } + mAllNotifications.remove(entry); + // Let's remove the children if this was a summary handleGroupSummaryRemoved(key); removeVisibleNotification(key); updateNotifications("removeNotificationInternal"); + mLeakDetector.trackGarbage(entry); removedByUser |= entryDismissed; mLogger.logNotifRemoved(entry.getKey(), removedByUser); @@ -502,7 +497,6 @@ public class NotificationEntryManager implements for (NotifCollectionListener listener : mNotifCollectionListeners) { listener.onEntryCleanUp(entry); } - mLeakDetector.trackGarbage(entry); } } } @@ -562,24 +556,17 @@ public class NotificationEntryManager implements Ranking ranking = new Ranking(); rankingMap.getRanking(key, ranking); - NotificationEntry entry = mPendingNotifications.get(key); - if (entry != null) { - entry.setSbn(notification); - } else { - entry = new NotificationEntry( - notification, - ranking, - mFgsFeatureController.isForegroundServiceDismissalEnabled(), - SystemClock.uptimeMillis()); - mAllNotifications.add(entry); - mLeakDetector.trackInstance(entry); - } - - abortExistingInflation(key, "addNotification"); - + NotificationEntry entry = new NotificationEntry( + notification, + ranking, + mFgsFeatureController.isForegroundServiceDismissalEnabled(), + SystemClock.uptimeMillis()); for (NotifCollectionListener listener : mNotifCollectionListeners) { listener.onEntryBind(entry, notification); } + mAllNotifications.add(entry); + + mLeakDetector.trackInstance(entry); for (NotifCollectionListener listener : mNotifCollectionListeners) { listener.onEntryInit(entry); @@ -594,6 +581,7 @@ public class NotificationEntryManager implements mInflationCallback); } + abortExistingInflation(key, "addNotification"); mPendingNotifications.put(key, entry); mLogger.logNotifAdded(entry.getKey()); for (NotificationEntryListener listener : mNotificationEntryListeners) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java index b1b6a1c12a0a..3517e245f624 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java @@ -92,6 +92,9 @@ public class PropertyAnimator { AnimatorListenerAdapter listener = properties.getAnimationFinishListener(property); if (currentValue.equals(newEndValue)) { // Skip the animation! + if (previousAnimator != null) { + previousAnimator.cancel(); + } if (listener != null) { listener.onAnimationEnd(null); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java index 3ee267362bc0..90d30dcf38ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java @@ -154,12 +154,14 @@ public final class NotifBindPipeline { * the real work once rather than repeatedly start and cancel it. */ private void requestPipelineRun(NotificationEntry entry) { + mLogger.logRequestPipelineRun(entry.getKey()); + final BindEntry bindEntry = getBindEntry(entry); if (bindEntry.row == null) { // Row is not managed yet but may be soon. Stop for now. + mLogger.logRequestPipelineRowNotSet(entry.getKey()); return; } - mLogger.logRequestPipelineRun(entry.getKey()); // Abort any existing pipeline run mStage.abortStage(entry, bindEntry.row); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt index 199730427aec..f26598db27a5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.row import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.LogLevel.WARNING import com.android.systemui.log.dagger.NotificationLog import javax.inject.Inject @@ -48,6 +49,14 @@ class NotifBindPipelineLogger @Inject constructor( }) } + fun logRequestPipelineRowNotSet(notifKey: String) { + buffer.log(TAG, WARNING, { + str1 = notifKey + }, { + "Row is not set so pipeline will not run. notif = $str1" + }) + } + fun logStartPipeline(notifKey: String) { buffer.log(TAG, INFO, { str1 = notifKey diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt index 65633a2e209f..e39a4a0c799f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt @@ -360,7 +360,6 @@ class NotificationSectionsManager @Inject internal constructor( } } } - else -> throw IllegalStateException("Cannot find section bucket for view") } prev = child diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java index ba323271faaa..de9c745cb357 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java @@ -37,8 +37,9 @@ import android.view.View; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.logging.UiEvent; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -60,7 +61,7 @@ public class RotationButtonController { private static final int NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION = 3; - private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); + private final UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); private final ViewRippler mViewRippler = new ViewRippler(); private @StyleRes int mStyleRes; @@ -323,7 +324,7 @@ public class RotationButtonController { } private void onRotateSuggestionClick(View v) { - mMetricsLogger.action(MetricsEvent.ACTION_ROTATION_SUGGESTION_ACCEPTED); + mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED); incrementNumAcceptedRotationSuggestionsIfNeeded(); setRotationLockedAtAngle(mLastRotationSuggestion); } @@ -345,7 +346,7 @@ public class RotationButtonController { private void showAndLogRotationSuggestion() { setRotateSuggestionButtonState(true /* visible */); rescheduleRotationTimeout(false /* reasonHover */); - mMetricsLogger.visible(MetricsEvent.ROTATION_SUGGESTION_SHOWN); + mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_SHOWN); } private boolean shouldOverrideUserLockPrefs(final int rotation) { @@ -474,4 +475,19 @@ public class RotationButtonController { } }; } + + enum RotationButtonEvent implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "The rotation button was shown") + ROTATION_SUGGESTION_SHOWN(206), + @UiEvent(doc = "The rotation button was clicked") + ROTATION_SUGGESTION_ACCEPTED(207); + + private final int mId; + RotationButtonEvent(int id) { + mId = id; + } + @Override public int getId() { + return mId; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java index 378dde284747..708b5a7a45f7 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java @@ -21,16 +21,17 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; -import android.os.Handler; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import javax.inject.Inject; @@ -49,8 +50,9 @@ public class ProximitySensor { private String mTag = null; @VisibleForTesting ProximityEvent mLastEvent; private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL; - private boolean mPaused; + @VisibleForTesting protected boolean mPaused; private boolean mRegistered; + private final AtomicBoolean mAlerting = new AtomicBoolean(); private SensorEventListener mSensorEventListener = new SensorEventListener() { @Override @@ -217,8 +219,12 @@ public class ProximitySensor { /** Update all listeners with the last value this class received from the sensor. */ public void alertListeners() { + if (mAlerting.getAndSet(true)) { + return; + } mListeners.forEach(proximitySensorListener -> proximitySensorListener.onSensorEvent(mLastEvent)); + mAlerting.set(false); } private void onSensorEvent(SensorEvent event) { @@ -239,14 +245,14 @@ public class ProximitySensor { public static class ProximityCheck implements Runnable { private final ProximitySensor mSensor; - private final Handler mHandler; + private final DelayableExecutor mDelayableExecutor; private List<Consumer<Boolean>> mCallbacks = new ArrayList<>(); @Inject - public ProximityCheck(ProximitySensor sensor, Handler handler) { + public ProximityCheck(ProximitySensor sensor, DelayableExecutor delayableExecutor) { mSensor = sensor; mSensor.setTag("prox_check"); - mHandler = handler; + mDelayableExecutor = delayableExecutor; mSensor.pause(); ProximitySensorListener listener = proximityEvent -> { mCallbacks.forEach( @@ -280,7 +286,7 @@ public class ProximitySensor { mCallbacks.add(callback); if (!mSensor.isRegistered()) { mSensor.resume(); - mHandler.postDelayed(this, timeoutMs); + mDelayableExecutor.executeDelayed(this, timeoutMs); } } } diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java index c7e9accce093..2968b92f51b7 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java @@ -178,6 +178,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged ValueAnimator mAnimation = null; int mRotation = Surface.ROTATION_0; boolean mImeShowing = false; + final Rect mImeFrame = new Rect(); PerDisplay(int displayId, int initialRotation) { mDisplayId = displayId; @@ -254,8 +255,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } - private int imeTop(InsetsSource imeSource, float surfaceOffset) { - return imeSource.getFrame().top + (int) surfaceOffset; + private int imeTop(float surfaceOffset) { + return mImeFrame.top + (int) surfaceOffset; } private void startAnimation(final boolean show, final boolean forceRestart) { @@ -263,6 +264,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged if (imeSource == null || mImeSourceControl == null) { return; } + // Set frame, but only if the new frame isn't empty -- this maintains continuity + final Rect newFrame = imeSource.getFrame(); + if (newFrame.height() != 0) { + mImeFrame.set(newFrame); + } mHandler.post(() -> { if (DEBUG) { Slog.d(TAG, "Run startAnim show:" + show + " was:" @@ -284,7 +290,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } final float defaultY = mImeSourceControl.getSurfacePosition().y; final float x = mImeSourceControl.getSurfacePosition().x; - final float hiddenY = defaultY + imeSource.getFrame().height(); + final float hiddenY = defaultY + mImeFrame.height(); final float shownY = defaultY; final float startY = show ? hiddenY : shownY; final float endY = show ? shownY : hiddenY; @@ -306,7 +312,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged SurfaceControl.Transaction t = mTransactionPool.acquire(); float value = (float) animation.getAnimatedValue(); t.setPosition(mImeSourceControl.getLeash(), x, value); - dispatchPositionChanged(mDisplayId, imeTop(imeSource, value), t); + dispatchPositionChanged(mDisplayId, imeTop(value), t); t.apply(); mTransactionPool.release(t); }); @@ -319,11 +325,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged t.setPosition(mImeSourceControl.getLeash(), x, startY); if (DEBUG) { Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:" - + imeTop(imeSource, hiddenY) + "->" + imeTop(imeSource, shownY) + + imeTop(hiddenY) + "->" + imeTop(shownY) + " showing:" + (mAnimationDirection == DIRECTION_SHOW)); } - dispatchStartPositioning(mDisplayId, imeTop(imeSource, hiddenY), - imeTop(imeSource, shownY), mAnimationDirection == DIRECTION_SHOW, + dispatchStartPositioning(mDisplayId, imeTop(hiddenY), + imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, t); if (mAnimationDirection == DIRECTION_SHOW) { t.show(mImeSourceControl.getLeash()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index 50b7af27f7d9..e0049d1349f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -19,6 +19,7 @@ package com.android.systemui.appops; import static junit.framework.TestCase.assertFalse; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -29,9 +30,8 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static java.lang.Thread.sleep; - import android.app.AppOpsManager; +import android.os.Looper; import android.os.UserHandle; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -229,12 +229,7 @@ public class AppOpsControllerTest extends SysuiTestCase { @Test public void testActiveOpNotRemovedAfterNoted() throws InterruptedException { // Replaces the timeout delay with 5 ms - AppOpsControllerImpl.H testHandler = mController.new H(mTestableLooper.getLooper()) { - @Override - public void scheduleRemoval(AppOpItem item, long timeToRemoval) { - super.scheduleRemoval(item, 5L); - } - }; + TestHandler testHandler = new TestHandler(mTestableLooper.getLooper()); mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); mController.setBGHandler(testHandler); @@ -245,6 +240,10 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED); + // Check that we "scheduled" the removal. Don't actually schedule until we are ready to + // process messages at a later time. + assertNotNull(testHandler.mDelayScheduled); + mTestableLooper.processAllMessages(); List<AppOpItem> list = mController.getActiveAppOps(); verify(mCallback).onActiveStateChanged( @@ -253,8 +252,8 @@ public class AppOpsControllerTest extends SysuiTestCase { // Duplicates are not removed between active and noted assertEquals(2, list.size()); - sleep(10L); - + // Now is later, so we can schedule delayed messages. + testHandler.scheduleDelayed(); mTestableLooper.processAllMessages(); verify(mCallback, never()).onActiveStateChanged( @@ -321,4 +320,24 @@ public class AppOpsControllerTest extends SysuiTestCase { verify(mCallback).onActiveStateChanged( AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); } + + private class TestHandler extends AppOpsControllerImpl.H { + TestHandler(Looper looper) { + mController.super(looper); + } + + Runnable mDelayScheduled; + + void scheduleDelayed() { + if (mDelayScheduled != null) { + mDelayScheduled.run(); + mDelayScheduled = null; + } + } + + @Override + public void scheduleRemoval(AppOpItem item, long timeToRemoval) { + mDelayScheduled = () -> super.scheduleRemoval(item, 0L); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java index 317500cf5b02..a5675360a57e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -18,6 +18,7 @@ package com.android.systemui.doze; import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN; +import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; @@ -46,6 +47,7 @@ import com.android.systemui.doze.DozeSensors.TriggerSensor; import com.android.systemui.plugins.SensorManagerPlugin; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.sensors.AsyncSensorManager; +import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.wakelock.WakeLock; import org.junit.Before; @@ -82,7 +84,7 @@ public class DozeSensorsTest extends SysuiTestCase { @Mock private DozeLog mDozeLog; @Mock - private Sensor mProximitySensor; + private ProximitySensor mProximitySensor; private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener; private TestableLooper mTestableLooper; private DozeSensors mDozeSensors; @@ -93,7 +95,6 @@ public class DozeSensorsTest extends SysuiTestCase { mTestableLooper = TestableLooper.get(this); when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L); when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true); - when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(mProximitySensor); doAnswer(invocation -> { ((Runnable) invocation.getArgument(0)).run(); return null; @@ -103,10 +104,9 @@ public class DozeSensorsTest extends SysuiTestCase { @Test public void testRegisterProx() { - // We should not register with the sensor manager initially. - verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt()); + assertFalse(mProximitySensor.isRegistered()); mDozeSensors.setProxListening(true); - verify(mSensorManager).registerListener(any(), any(Sensor.class), anyInt()); + verify(mProximitySensor).resume(); } @Test @@ -169,7 +169,8 @@ public class DozeSensorsTest extends SysuiTestCase { TestableDozeSensors() { super(getContext(), mAlarmManager, mSensorManager, mDozeParameters, - mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog); + mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog, + mProximitySensor); for (TriggerSensor sensor : mSensors) { if (sensor instanceof PluginSensor && ((PluginSensor) sensor).mPluginSensor.getType() diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index debc9d6430e0..73aaeffd6044 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -31,7 +31,6 @@ import android.app.AlarmManager; import android.hardware.Sensor; import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; -import android.os.Looper; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; @@ -42,10 +41,12 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.sensors.FakeProximitySensor; import com.android.systemui.util.sensors.FakeSensorManager; import com.android.systemui.util.sensors.ProximitySensor; +import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.wakelock.WakeLock; import com.android.systemui.util.wakelock.WakeLockFake; @@ -75,6 +76,7 @@ public class DozeTriggersTest extends SysuiTestCase { private FakeSensorManager mSensors; private Sensor mTapSensor; private FakeProximitySensor mProximitySensor; + private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); @Before public void setUp() throws Exception { @@ -89,7 +91,7 @@ public class DozeTriggersTest extends SysuiTestCase { mProximitySensor = new FakeProximitySensor(getContext().getResources(), asyncSensorManager); mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters, - asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true, + asyncSensorManager, mFakeExecutor, wakeLock, true, mDockManager, mProximitySensor, mock(DozeLog.class), mBroadcastDispatcher); waitForSensorManager(); } @@ -111,9 +113,8 @@ public class DozeTriggersTest extends SysuiTestCase { verify(mMachine, never()).requestState(any()); verify(mMachine, never()).requestPulse(anyInt()); - captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */); - waitForSensorManager(); mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(false, 2)); + captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */); mProximitySensor.alertListeners(); verify(mMachine).requestPulse(anyInt()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index ee7733aa2be7..4a8598009381 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -53,6 +53,7 @@ import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.controls.controller.ControlsController; import com.android.systemui.controls.management.ControlsListingController; import com.android.systemui.controls.ui.ControlsUiController; +import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.GlobalActions; import com.android.systemui.statusbar.BlurUtils; @@ -107,6 +108,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase { @Mock private UiEventLogger mUiEventLogger; @Mock private RingerModeTracker mRingerModeTracker; @Mock private RingerModeLiveData mRingerModeLiveData; + @Mock private SysUiState mSysUiState; @Mock private Handler mHandler; private TestableLooper mTestableLooper; @@ -150,6 +152,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase { mControlsController, mUiEventLogger, mRingerModeTracker, + mSysUiState, mHandler ); mGlobalActionsDialog.setZeroDialogPressDelayForTesting(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt new file mode 100644 index 000000000000..60d15a8fb480 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import android.content.res.ColorStateList +import android.graphics.Color +import android.media.MediaMetadata +import android.media.session.MediaSession +import android.media.session.PlaybackState +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.SeekBar +import android.widget.TextView + +import androidx.constraintlayout.motion.widget.MotionLayout +import androidx.constraintlayout.motion.widget.MotionScene +import androidx.constraintlayout.widget.ConstraintSet +import androidx.test.filters.SmallTest + +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock + +import com.google.common.truth.Truth.assertThat + +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` as whenever + +import java.util.ArrayList + +private const val KEY = "TEST_KEY" +private const val APP = "APP" +private const val BG_COLOR = Color.RED +private const val PACKAGE = "PKG" +private const val ARTIST = "ARTIST" +private const val TITLE = "TITLE" +private const val DEVICE_NAME = "DEVICE_NAME" +private const val SESSION_KEY = "SESSION_KEY" +private const val SESSION_ARTIST = "SESSION_ARTIST" +private const val SESSION_TITLE = "SESSION_TITLE" + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class MediaControlPanelTest : SysuiTestCase() { + + private lateinit var player: MediaControlPanel + + private lateinit var fgExecutor: FakeExecutor + private lateinit var bgExecutor: FakeExecutor + @Mock private lateinit var activityStarter: ActivityStarter + + @Mock private lateinit var holder: PlayerViewHolder + @Mock private lateinit var motion: MotionLayout + private lateinit var background: TextView + private lateinit var appIcon: ImageView + private lateinit var appName: TextView + private lateinit var albumView: ImageView + private lateinit var titleText: TextView + private lateinit var artistText: TextView + private lateinit var seamless: ViewGroup + private lateinit var seamlessIcon: ImageView + private lateinit var seamlessText: TextView + private lateinit var seekBar: SeekBar + private lateinit var elapsedTimeView: TextView + private lateinit var totalTimeView: TextView + private lateinit var action0: ImageButton + private lateinit var action1: ImageButton + private lateinit var action2: ImageButton + private lateinit var action3: ImageButton + private lateinit var action4: ImageButton + + private lateinit var session: MediaSession + + @Before + fun setUp() { + fgExecutor = FakeExecutor(FakeSystemClock()) + bgExecutor = FakeExecutor(FakeSystemClock()) + + activityStarter = mock(ActivityStarter::class.java) + + player = MediaControlPanel(context, null, fgExecutor, bgExecutor, activityStarter) + + // Mock out a view holder for the player to attach to. + holder = mock(PlayerViewHolder::class.java) + motion = mock(MotionLayout::class.java) + val trans: ArrayList<MotionScene.Transition> = ArrayList() + trans.add(mock(MotionScene.Transition::class.java)) + whenever(motion.definedTransitions).thenReturn(trans) + val constraintSet = mock(ConstraintSet::class.java) + whenever(motion.getConstraintSet(R.id.expanded)).thenReturn(constraintSet) + whenever(motion.getConstraintSet(R.id.collapsed)).thenReturn(constraintSet) + whenever(holder.player).thenReturn(motion) + background = TextView(context) + whenever(holder.background).thenReturn(background) + appIcon = ImageView(context) + whenever(holder.appIcon).thenReturn(appIcon) + appName = TextView(context) + whenever(holder.appName).thenReturn(appName) + albumView = ImageView(context) + whenever(holder.albumView).thenReturn(albumView) + titleText = TextView(context) + whenever(holder.titleText).thenReturn(titleText) + artistText = TextView(context) + whenever(holder.artistText).thenReturn(artistText) + seamless = FrameLayout(context) + whenever(holder.seamless).thenReturn(seamless) + seamlessIcon = ImageView(context) + whenever(holder.seamlessIcon).thenReturn(seamlessIcon) + seamlessText = TextView(context) + whenever(holder.seamlessText).thenReturn(seamlessText) + seekBar = SeekBar(context) + whenever(holder.seekBar).thenReturn(seekBar) + elapsedTimeView = TextView(context) + whenever(holder.elapsedTimeView).thenReturn(elapsedTimeView) + totalTimeView = TextView(context) + whenever(holder.totalTimeView).thenReturn(totalTimeView) + action0 = ImageButton(context) + whenever(holder.action0).thenReturn(action0) + action1 = ImageButton(context) + whenever(holder.action1).thenReturn(action1) + action2 = ImageButton(context) + whenever(holder.action2).thenReturn(action2) + action3 = ImageButton(context) + whenever(holder.action3).thenReturn(action3) + action4 = ImageButton(context) + whenever(holder.action4).thenReturn(action4) + + // Create media session + val metadataBuilder = MediaMetadata.Builder().apply { + putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST) + putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE) + } + val playbackBuilder = PlaybackState.Builder().apply { + setState(PlaybackState.STATE_PAUSED, 6000L, 1f) + setActions(PlaybackState.ACTION_PLAY) + } + session = MediaSession(context, SESSION_KEY).apply { + setMetadata(metadataBuilder.build()) + setPlaybackState(playbackBuilder.build()) + } + session.setActive(true) + } + + @After + fun tearDown() { + session.release() + player.onDestroy() + } + + @Test + fun bindWhenUnattached() { + val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), + emptyList(), PACKAGE, null, null) + player.bind(state) + assertThat(player.isPlaying()).isFalse() + } + + @Test + fun bindText() { + player.attach(holder) + val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), + emptyList(), PACKAGE, session.getSessionToken(), null) + player.bind(state) + assertThat(appName.getText()).isEqualTo(APP) + assertThat(titleText.getText()).isEqualTo(TITLE) + assertThat(artistText.getText()).isEqualTo(ARTIST) + } + + @Test + fun bindBackgroundColor() { + player.attach(holder) + val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), + emptyList(), PACKAGE, session.getSessionToken(), null) + player.bind(state) + assertThat(background.getBackgroundTintList()).isEqualTo(ColorStateList.valueOf(BG_COLOR)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/PlayerViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/PlayerViewHolderTest.kt new file mode 100644 index 000000000000..767852582dc3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/PlayerViewHolderTest.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.FrameLayout + +import androidx.test.filters.SmallTest + +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat + +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Tests for PlayerViewHolder. + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class PlayerViewHolderTest : SysuiTestCase() { + + private lateinit var inflater: LayoutInflater + private lateinit var parent: ViewGroup + + @Before + fun setUp() { + inflater = LayoutInflater.from(context) + parent = FrameLayout(context) + } + + @Test + fun create() { + val holder = PlayerViewHolder.create(inflater, parent) + assertThat(holder.player).isNotNull() + } + + @Test + fun backgroundIsIlluminationDrawable() { + val holder = PlayerViewHolder.create(inflater, parent) + assertThat(holder.background.background as IlluminationDrawable).isNotNull() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt index 58ee79e39279..75018df023cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt @@ -22,7 +22,6 @@ import android.view.View import android.widget.SeekBar import android.widget.TextView import androidx.test.filters.SmallTest -import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -38,23 +37,21 @@ import org.mockito.Mockito.`when` as whenever public class SeekBarObserverTest : SysuiTestCase() { private lateinit var observer: SeekBarObserver - @Mock private lateinit var mockView: View + @Mock private lateinit var mockHolder: PlayerViewHolder private lateinit var seekBarView: SeekBar private lateinit var elapsedTimeView: TextView private lateinit var totalTimeView: TextView @Before fun setUp() { - mockView = mock(View::class.java) + mockHolder = mock(PlayerViewHolder::class.java) seekBarView = SeekBar(context) elapsedTimeView = TextView(context) totalTimeView = TextView(context) - whenever<SeekBar>( - mockView.findViewById(R.id.media_progress_bar)).thenReturn(seekBarView) - whenever<TextView>( - mockView.findViewById(R.id.media_elapsed_time)).thenReturn(elapsedTimeView) - whenever<TextView>(mockView.findViewById(R.id.media_total_time)).thenReturn(totalTimeView) - observer = SeekBarObserver(mockView) + whenever(mockHolder.seekBar).thenReturn(seekBarView) + whenever(mockHolder.elapsedTimeView).thenReturn(elapsedTimeView) + whenever(mockHolder.totalTimeView).thenReturn(totalTimeView) + observer = SeekBarObserver(mockHolder) } @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 a5a5f81bdffe..d583048fbb26 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 @@ -210,28 +210,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { } @Test - public void testAddNotification_noDuplicateEntriesCreated() { - // GIVEN a notification has been added - mEntryManager.addNotification(mSbn, mRankingMap); - - // WHEN the same notification is added multiple times before the previous entry (with - // the same key) didn't finish inflating - mEntryManager.addNotification(mSbn, mRankingMap); - mEntryManager.addNotification(mSbn, mRankingMap); - mEntryManager.addNotification(mSbn, mRankingMap); - - // THEN getAllNotifs() only contains exactly one notification with this key - int count = 0; - for (NotificationEntry entry : mEntryManager.getAllNotifs()) { - if (entry.getKey().equals(mSbn.getKey())) { - count++; - } - } - assertEquals("Should only be one entry with key=" + mSbn.getKey() + " in mAllNotifs. " - + "Instead there are " + count, 1, count); - } - - @Test public void testAddNotification_setsUserSentiment() { mEntryManager.addNotification(mSbn, mRankingMap); diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java index 31d884c38f58..bd697fe394b5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java @@ -20,6 +20,7 @@ import android.content.res.Resources; public class FakeProximitySensor extends ProximitySensor { private boolean mAvailable; + private boolean mRegistered; public FakeProximitySensor(Resources resources, AsyncSensorManager sensorManager) { super(resources, sensorManager); @@ -35,17 +36,22 @@ public class FakeProximitySensor extends ProximitySensor { } @Override + public boolean isRegistered() { + return mRegistered; + } + + @Override public boolean getSensorAvailable() { return mAvailable; } @Override protected void registerInternal() { - // no-op + mRegistered = !mPaused; } @Override protected void unregisterInternal() { - // no-op + mRegistered = false; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java new file mode 100644 index 000000000000..7221095a1eaf --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.sensors; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.os.Handler; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.function.Consumer; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class ProximityCheckTest extends SysuiTestCase { + + private FakeProximitySensor mFakeProximitySensor; + private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); + + private TestableCallback mTestableCallback = new TestableCallback(); + + private ProximitySensor.ProximityCheck mProximityCheck; + + @Before + public void setUp() throws Exception { + AsyncSensorManager asyncSensorManager = + new AsyncSensorManager(new FakeSensorManager(mContext), null, new Handler()); + mFakeProximitySensor = new FakeProximitySensor(mContext.getResources(), asyncSensorManager); + + mProximityCheck = new ProximitySensor.ProximityCheck(mFakeProximitySensor, mFakeExecutor); + } + + @Test + public void testCheck() { + mProximityCheck.check(100, mTestableCallback); + + assertNull(mTestableCallback.mLastResult); + + mFakeProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(true, 0)); + mFakeProximitySensor.alertListeners(); + + assertTrue(mTestableCallback.mLastResult); + } + + @Test + public void testTimeout() { + mProximityCheck.check(100, mTestableCallback); + + assertTrue(mFakeProximitySensor.isRegistered()); + + mFakeExecutor.advanceClockToNext(); + mFakeExecutor.runAllReady(); + + assertFalse(mFakeProximitySensor.isRegistered()); + } + + private static class TestableCallback implements Consumer<Boolean> { + Boolean mLastResult; + @Override + public void accept(Boolean result) { + mLastResult = result; + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java index 526fba726e9d..914790b53a82 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java @@ -219,6 +219,26 @@ public class ProximitySensorTest extends SysuiTestCase { waitForSensorManager(); } + @Test + public void testPreventRecursiveAlert() { + TestableListener listenerA = new TestableListener() { + @Override + public void onSensorEvent(ProximitySensor.ProximityEvent proximityEvent) { + super.onSensorEvent(proximityEvent); + if (mCallCount < 2) { + mProximitySensor.alertListeners(); + } + } + }; + + mProximitySensor.register(listenerA); + + mProximitySensor.alertListeners(); + + assertEquals(1, listenerA.mCallCount); + } + + class TestableListener implements ProximitySensor.ProximitySensorListener { ProximitySensor.ProximityEvent mLastEvent; int mCallCount = 0; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2b86d7fe057e..4ace676eca7e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -337,6 +337,7 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.function.HexFunction; import com.android.internal.util.function.QuadFunction; import com.android.internal.util.function.TriFunction; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.AlarmManagerInternal; import com.android.server.AttributeCache; import com.android.server.DeviceIdleInternal; @@ -2098,6 +2099,7 @@ public class ActivityManagerService extends IActivityManager.Stub } ServiceManager.addService("permission", new PermissionController(this)); ServiceManager.addService("processinfo", new ProcessInfoService(this)); + ServiceManager.addService("cacheinfo", new CacheBinder(this)); ApplicationInfo info = mContext.getPackageManager().getApplicationInfo( "android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY); @@ -2191,16 +2193,18 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } - - if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, - "meminfo", pw)) return; - PriorityDump.dump(mPriorityDumper, fd, pw, args); + try { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(false); + } - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); + if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, + "meminfo", pw)) return; + PriorityDump.dump(mPriorityDumper, fd, pw, args); + } finally { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(true); + } } } } @@ -2213,16 +2217,18 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } - - if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, - "gfxinfo", pw)) return; - mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args); + try { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(false); + } - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); + if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, + "gfxinfo", pw)) return; + mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args); + } finally { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(true); + } } } } @@ -2235,16 +2241,18 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } - - if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, - "dbinfo", pw)) return; - mActivityManagerService.dumpDbInfo(fd, pw, args); + try { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(false); + } - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); + if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, + "dbinfo", pw)) return; + mActivityManagerService.dumpDbInfo(fd, pw, args); + } finally { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(true); + } } } } @@ -2280,6 +2288,34 @@ public class ActivityManagerService extends IActivityManager.Stub } } + static class CacheBinder extends Binder { + ActivityManagerService mActivityManagerService; + + CacheBinder(ActivityManagerService activityManagerService) { + mActivityManagerService = activityManagerService; + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + try { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(false); + } + + if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, + "cacheinfo", pw)) { + return; + } + + mActivityManagerService.dumpBinderCacheContents(fd, pw, args); + } finally { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(true); + } + } + } + } + public static final class Lifecycle extends SystemService { private final ActivityManagerService mService; private static ActivityTaskManagerService sAtm; @@ -12722,6 +12758,39 @@ public class ActivityManagerService extends IActivityManager.Stub } } + final void dumpBinderCacheContents(FileDescriptor fd, PrintWriter pw, String[] args) { + ArrayList<ProcessRecord> procs = collectProcesses(pw, 0, false, args); + if (procs == null) { + pw.println("No process found for: " + args[0]); + return; + } + + pw.println("Per-process Binder Cache Contents"); + + for (int i = procs.size() - 1; i >= 0; i--) { + ProcessRecord r = procs.get(i); + if (r.thread != null) { + pw.println("\n\n** Cache info for pid " + r.pid + " [" + r.processName + "] **"); + pw.flush(); + try { + TransferPipe tp = new TransferPipe(); + try { + r.thread.dumpCacheInfo(tp.getWriteFd(), args); + tp.go(fd); + } finally { + tp.kill(); + } + } catch (IOException e) { + pw.println("Failure while dumping the app " + r); + pw.flush(); + } catch (RemoteException e) { + pw.println("Got a RemoteException while dumping the app " + r); + pw.flush(); + } + } + } + } + final void dumpDbInfo(FileDescriptor fd, PrintWriter pw, String[] args) { ArrayList<ProcessRecord> procs = collectProcesses(pw, 0, false, args); if (procs == null) { @@ -18763,28 +18832,39 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) { - // We can find ourselves needing to check Uri permissions while - // already holding the WM lock, which means reaching back here for - // the AM lock would cause an inversion. The WM team has requested - // that we use the strategy below instead of shifting where Uri - // grants are calculated. - - // Since we could also arrive here while holding the AM lock, we - // can't always delegate the call through the handler, and we need - // to delicately dance between the deadlocks. - if (Thread.currentThread().holdsLock(ActivityManagerService.this)) { + final Object wmLock = mActivityTaskManager.getGlobalLock(); + if (Thread.currentThread().holdsLock(wmLock) + && !Thread.currentThread().holdsLock(ActivityManagerService.this)) { + // We can find ourselves needing to check Uri permissions while already holding the + // WM lock, which means reaching back here for the AM lock would cause an inversion. + // The WM team has requested that we use the strategy below instead of shifting + // where Uri grants are calculated. + synchronized (wmLock) { + final int[] result = new int[1]; + final Message msg = PooledLambda.obtainMessage( + LocalService::checkContentProviderUriPermission, + this, uri, userId, callingUid, modeFlags, wmLock, result); + mHandler.sendMessage(msg); + try { + wmLock.wait(); + } catch (InterruptedException ignore) { + + } + return result[0]; + } + } else { return ActivityManagerService.this.checkContentProviderUriPermission(uri, userId, callingUid, modeFlags); - } else { - final CompletableFuture<Integer> res = new CompletableFuture<>(); - mHandler.post(() -> { - res.complete(ActivityManagerService.this.checkContentProviderUriPermission(uri, - userId, callingUid, modeFlags)); - }); - try { - return res.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); + } + } + + void checkContentProviderUriPermission( + Uri uri, int userId, int callingUid, int modeFlags, Object wmLock, int[] result) { + synchronized (ActivityManagerService.this) { + synchronized (wmLock) { + result[0] = ActivityManagerService.this.checkContentProviderUriPermission( + uri, userId, callingUid, modeFlags); + wmLock.notify(); } } } diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index f9d204fa008e..43e3a04ad032 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -45,6 +45,7 @@ import com.android.server.ServiceThread; import java.io.FileOutputStream; import java.io.FileReader; +import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -114,6 +115,14 @@ public final class CachedAppOptimizer { } private PropertyChangedCallbackForTest mTestCallback; + // This interface is for functions related to the Process object that need a different + // implementation in the tests as we are not creating real processes when testing compaction. + @VisibleForTesting + interface ProcessDependencies { + long[] getRss(int pid); + void performCompaction(String action, int pid) throws IOException; + } + // Handler constants. static final int COMPACT_PROCESS_SOME = 1; static final int COMPACT_PROCESS_FULL = 2; @@ -215,13 +224,16 @@ public final class CachedAppOptimizer { @VisibleForTesting final Set<Integer> mProcStateThrottle; // Handler on which compaction runs. - private Handler mCompactionHandler; + @VisibleForTesting + Handler mCompactionHandler; private Handler mFreezeHandler; // Maps process ID to last compaction statistics for processes that we've fully compacted. Used // when evaluating throttles that we only consider for "full" compaction, so we don't store - // data for "some" compactions. - private Map<Integer, LastCompactionStats> mLastCompactionStats = + // data for "some" compactions. Uses LinkedHashMap to ensure insertion order is kept and + // facilitate removal of the oldest entry. + @VisibleForTesting + LinkedHashMap<Integer, LastCompactionStats> mLastCompactionStats = new LinkedHashMap<Integer, LastCompactionStats>() { @Override protected boolean removeEldestEntry(Map.Entry eldest) { @@ -233,17 +245,20 @@ public final class CachedAppOptimizer { private int mFullCompactionCount; private int mPersistentCompactionCount; private int mBfgsCompactionCount; + private final ProcessDependencies mProcessDependencies; public CachedAppOptimizer(ActivityManagerService am) { - mAm = am; - mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread", - THREAD_PRIORITY_FOREGROUND, true); - mProcStateThrottle = new HashSet<>(); + this(am, null, new DefaultProcessDependencies()); } @VisibleForTesting - CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback) { - this(am); + CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback, + ProcessDependencies processDependencies) { + mAm = am; + mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread", + THREAD_PRIORITY_FOREGROUND, true); + mProcStateThrottle = new HashSet<>(); + mProcessDependencies = processDependencies; mTestCallback = callback; } @@ -659,7 +674,8 @@ public final class CachedAppOptimizer { } } - private static final class LastCompactionStats { + @VisibleForTesting + static final class LastCompactionStats { private final long[] mRssAfterCompaction; LastCompactionStats(long[] rss) { @@ -712,9 +728,7 @@ public final class CachedAppOptimizer { lastCompactAction = proc.lastCompactAction; lastCompactTime = proc.lastCompactTime; - // remove rather than get so that insertion order will be updated when we - // put the post-compaction stats back into the map. - lastCompactionStats = mLastCompactionStats.remove(pid); + lastCompactionStats = mLastCompactionStats.get(pid); } if (pid == 0) { @@ -806,7 +820,7 @@ public final class CachedAppOptimizer { return; } - long[] rssBefore = Process.getRss(pid); + long[] rssBefore = mProcessDependencies.getRss(pid); long anonRssBefore = rssBefore[2]; if (rssBefore[0] == 0 && rssBefore[1] == 0 && rssBefore[2] == 0 @@ -863,16 +877,13 @@ public final class CachedAppOptimizer { default: break; } - try { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact " + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full") + ": " + name); long zramFreeKbBefore = Debug.getZramFreeKb(); - FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim"); - fos.write(action.getBytes()); - fos.close(); - long[] rssAfter = Process.getRss(pid); + mProcessDependencies.performCompaction(action, pid); + long[] rssAfter = mProcessDependencies.getRss(pid); long end = SystemClock.uptimeMillis(); long time = end - start; long zramFreeKbAfter = Debug.getZramFreeKb(); @@ -882,7 +893,6 @@ public final class CachedAppOptimizer { rssAfter[2] - rssBefore[2], rssAfter[3] - rssBefore[3], time, lastCompactAction, lastCompactTime, lastOomAdj, procState, zramFreeKbBefore, zramFreeKbAfter - zramFreeKbBefore); - // Note that as above not taking mPhenoTypeFlagLock here to avoid locking // on every single compaction for a flag that will seldom change and the // impact of reading the wrong value here is low. @@ -894,14 +904,14 @@ public final class CachedAppOptimizer { lastOomAdj, ActivityManager.processStateAmToProto(procState), zramFreeKbBefore, zramFreeKbAfter); } - synchronized (mAm) { proc.lastCompactTime = end; proc.lastCompactAction = pendingAction; } - if (action.equals(COMPACT_ACTION_FULL) || action.equals(COMPACT_ACTION_ANON)) { + // Remove entry and insert again to update insertion order. + mLastCompactionStats.remove(pid); mLastCompactionStats.put(pid, new LastCompactionStats(rssAfter)); } } catch (Exception e) { @@ -1018,4 +1028,23 @@ public final class CachedAppOptimizer { } } } + + /** + * Default implementation for ProcessDependencies, public vor visibility to OomAdjuster class. + */ + private static final class DefaultProcessDependencies implements ProcessDependencies { + // Get memory RSS from process. + @Override + public long[] getRss(int pid) { + return Process.getRss(pid); + } + + // Compact process. + @Override + public void performCompaction(String action, int pid) throws IOException { + try (FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim")) { + fos.write(action.getBytes()); + } + } + } } diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index a099606852d0..b9cd43d0803d 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -51,6 +51,11 @@ public abstract class BrightnessMappingStrategy { private static final float MAX_GRAD = 1.0f; private static final float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f; + // Constant that ensures that each step of the curve can increase by up to at least + // MIN_PERMISSABLE_INCREASE. Otherwise when the brightness is set to 0, the curve will never + // increase and will always be 0. + private static final float MIN_PERMISSABLE_INCREASE = 0.004f; + protected boolean mLoggingEnabled; private static final Plog PLOG = Plog.createSystemPlog(TAG); @@ -400,7 +405,9 @@ public abstract class BrightnessMappingStrategy { for (int i = idx+1; i < lux.length; i++) { float currLux = lux[i]; float currBrightness = brightness[i]; - float maxBrightness = prevBrightness * permissibleRatio(currLux, prevLux); + float maxBrightness = MathUtils.max( + prevBrightness * permissibleRatio(currLux, prevLux), + prevBrightness + MIN_PERMISSABLE_INCREASE); float newBrightness = MathUtils.constrain( currBrightness, prevBrightness, maxBrightness); if (newBrightness == currBrightness) { diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index a435f1e16b80..53205add0b38 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -60,7 +60,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider private Connection mActiveConnection; private boolean mConnectionReady; - private RouteDiscoveryPreference mPendingDiscoveryPreference = null; + private RouteDiscoveryPreference mLastDiscoveryPreference = null; MediaRoute2ProviderServiceProxy(@NonNull Context context, @NonNull ComponentName componentName, int userId) { @@ -98,11 +98,10 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider @Override public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) { + mLastDiscoveryPreference = discoveryPreference; if (mConnectionReady) { mActiveConnection.updateDiscoveryPreference(discoveryPreference); updateBinding(); - } else { - mPendingDiscoveryPreference = discoveryPreference; } } @@ -277,9 +276,8 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider private void onConnectionReady(Connection connection) { if (mActiveConnection == connection) { mConnectionReady = true; - if (mPendingDiscoveryPreference != null) { - updateDiscoveryPreference(mPendingDiscoveryPreference); - mPendingDiscoveryPreference = null; + if (mLastDiscoveryPreference != null) { + updateDiscoveryPreference(mLastDiscoveryPreference); } } } diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index fdee9f86bfaf..d7e724780c94 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -45,7 +45,7 @@ import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; -import android.util.Log; +import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -58,7 +58,8 @@ import java.util.Objects; // TODO: check thread safety. We may need to use lock to protect variables. class SystemMediaRoute2Provider extends MediaRoute2Provider { private static final String TAG = "MR2SystemProvider"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + // TODO(b/156996903): Revert it when releasing the framework. + private static final boolean DEBUG = true; static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE"; static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE"; @@ -269,7 +270,11 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { builder.addRoute(route); } } - setProviderState(builder.build()); + MediaRoute2ProviderInfo providerInfo = builder.build(); + setProviderState(providerInfo); + if (DEBUG) { + Slog.d(TAG, "Updating system provider info : " + providerInfo); + } } /** @@ -327,6 +332,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (Objects.equals(oldSessionInfo, newSessionInfo)) { return false; } else { + if (DEBUG) { + Slog.d(TAG, "Updating system routing session info : " + newSessionInfo); + } mSessionInfos.clear(); mSessionInfos.add(newSessionInfo); mDefaultSessionInfo = new RoutingSessionInfo.Builder( diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 38c65f11a717..9d56d817440b 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -1936,6 +1936,9 @@ public class PreferencesHelper implements RankingConfig { event.writeInt(channel.getImportance()); event.writeInt(channel.getUserLockedFields()); event.writeBoolean(channel.isDeleted()); + event.writeBoolean(channel.getConversationId() != null); + event.writeBoolean(channel.isDemoted()); + event.writeBoolean(channel.isImportantConversation()); events.add(event.build()); } } diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 70d1adecc6f3..f9d805e57305 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -417,7 +417,7 @@ public class AppsFilter { public void grantImplicitAccess(int recipientUid, int visibleUid) { if (recipientUid != visibleUid && mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) { - Slog.wtf(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid); + Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid); } } @@ -720,7 +720,7 @@ public class AppsFilter { return false; } if (callingSetting == null) { - Slog.wtf(TAG, "No setting found for non system uid " + callingUid); + Slog.w(TAG, "No setting found for non system uid " + callingUid); return true; } final PackageSetting callingPkgSetting; @@ -760,7 +760,7 @@ public class AppsFilter { final AndroidPackage targetPkg = targetPkgSetting.pkg; if (targetPkg == null) { if (DEBUG_LOGGING) { - Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null"); + Slog.w(TAG, "shouldFilterApplication: " + "targetPkg is null"); } return true; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 3e587bf01521..f3619f22d231 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3181,7 +3181,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - final int cachedSystemApps = PackageParser.sCachedPackageReadCount.get(); + final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get(); // Remove any shared userIDs that have no associated packages mSettings.pruneSharedUsersLPw(); @@ -3315,7 +3315,7 @@ public class PackageManagerService extends IPackageManager.Stub // This must be done last to ensure all stubs are replaced or disabled. installSystemStubPackages(stubSystemApps, scanFlags); - final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get() + final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get() - cachedSystemApps; final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime; @@ -13221,7 +13221,9 @@ public class PackageManagerService extends IPackageManager.Stub private void enforceCanSetPackagesSuspendedAsUser(String callingPackage, int callingUid, int userId, String callingMethod) { - if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID) { + if (callingUid == Process.ROOT_UID + // Need to compare app-id to allow system dialogs access on secondary users + || UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) { return; } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 7d49f788c063..163504cb5011 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -124,6 +124,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.RoSystemProperties; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.DumpUtils; import com.android.internal.util.IntPair; import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledLambda; @@ -421,6 +422,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) { + return; + } + mContext.getSystemService(PermissionControllerManager.class).dump(fd, args); } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 3336697ef359..f075790a2fa0 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -301,10 +301,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) { String packageName = intent.getData().getSchemeSpecificPart(); - if (LOCAL_LOGV) { - Slog.v(TAG, "broadcast=ACTION_PACKAGE_FULLY_REMOVED" - + " pkg=" + packageName); - } + Slog.i(TAG, "broadcast=ACTION_PACKAGE_FULLY_REMOVED pkg=" + packageName); onPackageFullyRemoved(packageName); } } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index a859a42d8a8f..9871623becf5 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -321,17 +321,10 @@ public class StatsPullAtomService extends SystemService { try { switch (atomTag) { case FrameworkStatsLog.WIFI_BYTES_TRANSFER: - return pullDataBytesTransfer(atomTag, data, TRANSPORT_WIFI, - /*withFgbg=*/ false); case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: - return pullDataBytesTransfer(atomTag, data, TRANSPORT_WIFI, - /*withFgbg=*/ true); case FrameworkStatsLog.MOBILE_BYTES_TRANSFER: - return pullDataBytesTransfer(atomTag, data, TRANSPORT_CELLULAR, - /*withFgbg=*/ false); case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: - return pullDataBytesTransfer(atomTag, data, TRANSPORT_CELLULAR, - /*withFgbg=*/ true); + return pullDataBytesTransfer(atomTag, data); case FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER: return pullBluetoothBytesTransfer(atomTag, data); case FrameworkStatsLog.KERNEL_WAKELOCK: @@ -639,10 +632,14 @@ public class StatsPullAtomService extends SystemService { Slog.d(TAG, "Registering NetworkStats pullers with statsd"); } // Initialize NetworkStats baselines. - mNetworkStatsBaselines.addAll(collectWifiBytesTransferSnapshot(/*withFgbg=*/ false)); - mNetworkStatsBaselines.addAll(collectWifiBytesTransferSnapshot(/*withFgbg=*/ true)); - mNetworkStatsBaselines.addAll(collectMobileBytesTransferSnapshot(/*withFgbg=*/ false)); - mNetworkStatsBaselines.addAll(collectMobileBytesTransferSnapshot(/*withFgbg=*/ true)); + mNetworkStatsBaselines.addAll( + collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER)); + mNetworkStatsBaselines.addAll( + collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG)); + mNetworkStatsBaselines.addAll( + collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.MOBILE_BYTES_TRANSFER)); + mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom( + FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG)); registerWifiBytesTransfer(); registerWifiBytesTransferBackground(); @@ -800,36 +797,42 @@ public class StatsPullAtomService extends SystemService { } @NonNull - private List<NetworkStatsExt> collectWifiBytesTransferSnapshot(boolean withFgbg) { - final List<NetworkStatsExt> ret = new ArrayList<>(); - final NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard(); - final NetworkStats stats = getUidNetworkStatsSnapshot(template, withFgbg); - if (stats != null) { - ret.add(new NetworkStatsExt(stats, TRANSPORT_WIFI, withFgbg)); - } - return ret; - } - - // Get a snapshot of mobile data usage. The snapshot contains NetworkStats with its associated + private List<NetworkStatsExt> collectNetworkStatsSnapshotForAtom(int atomTag) { + switch(atomTag) { + case FrameworkStatsLog.WIFI_BYTES_TRANSFER: + return collectUidNetworkStatsSnapshot(TRANSPORT_WIFI, /*withFgbg=*/false); + case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: + return collectUidNetworkStatsSnapshot(TRANSPORT_WIFI, /*withFgbg=*/true); + case FrameworkStatsLog.MOBILE_BYTES_TRANSFER: + return collectUidNetworkStatsSnapshot(TRANSPORT_CELLULAR, /*withFgbg=*/false); + case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: + return collectUidNetworkStatsSnapshot(TRANSPORT_CELLULAR, /*withFgbg=*/true); + default: + throw new IllegalArgumentException("Unknown atomTag " + atomTag); + } + } + + // Get a snapshot of Uid NetworkStats. The snapshot contains NetworkStats with its associated // information, and wrapped by a list since multiple NetworkStatsExt objects might be collected. - // TODO: Slice NetworkStats to multiple objects by RAT type or subscription. @NonNull - private List<NetworkStatsExt> collectMobileBytesTransferSnapshot(boolean withFgbg) { + private List<NetworkStatsExt> collectUidNetworkStatsSnapshot(int transport, boolean withFgbg) { final List<NetworkStatsExt> ret = new ArrayList<>(); - final NetworkTemplate template = - NetworkTemplate.buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL); + final NetworkTemplate template = (transport == TRANSPORT_CELLULAR + ? NetworkTemplate.buildTemplateMobileWithRatType( + /*subscriptionId=*/null, NETWORK_TYPE_ALL) + : NetworkTemplate.buildTemplateWifiWildcard()); + final NetworkStats stats = getUidNetworkStatsSnapshot(template, withFgbg); if (stats != null) { - ret.add(new NetworkStatsExt(stats, TRANSPORT_CELLULAR, withFgbg)); + ret.add(new NetworkStatsExt(stats, transport, withFgbg)); } return ret; } + private int pullDataBytesTransfer( - int atomTag, @NonNull List<StatsEvent> pulledData, int transport, boolean withFgbg) { - final List<NetworkStatsExt> current = - (transport == TRANSPORT_CELLULAR ? collectMobileBytesTransferSnapshot(withFgbg) - : collectWifiBytesTransferSnapshot(withFgbg)); + int atomTag, @NonNull List<StatsEvent> pulledData) { + final List<NetworkStatsExt> current = collectNetworkStatsSnapshotForAtom(atomTag); if (current == null) { Slog.e(TAG, "current snapshot is null for " + atomTag + ", return."); @@ -844,7 +847,7 @@ public class StatsPullAtomService extends SystemService { // skip reporting anything since the snapshot is invalid. if (baseline == null) { Slog.e(TAG, "baseline is null for " + atomTag + ", transport=" - + item.transport + " , withFgbg=" + withFgbg + ", return."); + + item.transport + " , withFgbg=" + item.withFgbg + ", return."); return StatsManager.PULL_SKIP; } final NetworkStatsExt diff = new NetworkStatsExt(item.stats.subtract( diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index 72cdf4afb007..15b6a8df0151 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -114,7 +114,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { private static final String TAG = "UriGrantsManagerService"; // Maximum number of persisted Uri grants a package is allowed private static final int MAX_PERSISTED_URI_GRANTS = 128; - private static final boolean ENABLE_DYNAMIC_PERMISSIONS = false; + private static final boolean ENABLE_DYNAMIC_PERMISSIONS = true; private final Object mLock = new Object(); private final H mH; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0598680f8c5d..b5b82d39b921 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3589,7 +3589,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom, - WindowContainer boundary) { + ActivityRecord boundary) { return callback.test(this) ? this : null; } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 2f868d949970..9b9b61340332 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -2708,7 +2708,9 @@ class ActivityStack extends Task { */ @Nullable private ActivityRecord getOccludingActivityAbove(ActivityRecord activity) { - return getActivity((ar) -> ar.occludesParent(), true /* traverseTopToBottom */, activity); + ActivityRecord top = getActivity((ar) -> ar.occludesParent(), + true /* traverseTopToBottom */, activity); + return top != activity ? top : null; } boolean willActivityBeVisible(IBinder token) { diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index dfa3fe088770..c28d47cdbe80 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -19,7 +19,6 @@ package com.android.server.wm; import static android.app.ActivityManager.START_SUCCESS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL; @@ -193,9 +192,7 @@ public class ActivityStartController { final ActivityStack homeStack; try { // Make sure home stack exists on display area. - // TODO(b/153624902): Replace with TaskDisplayArea#getOrCreateRootHomeTask() - homeStack = taskDisplayArea.getOrCreateStack(WINDOWING_MODE_UNDEFINED, - ACTIVITY_TYPE_HOME, ON_TOP); + homeStack = taskDisplayArea.getOrCreateRootHomeTask(ON_TOP); } finally { mSupervisor.endDeferResume(); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 78e4237eb4a7..fdbb2b25bd39 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3319,7 +3319,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void resizeTask(int taskId, Rect bounds, int resizeMode) { + public boolean resizeTask(int taskId, Rect bounds, int resizeMode) { mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()"); long ident = Binder.clearCallingIdentity(); try { @@ -3328,10 +3328,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { MATCH_TASK_IN_STACKS_ONLY); if (task == null) { Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found"); - return; + return false; } if (!task.getWindowConfiguration().canResizeTask()) { - throw new IllegalArgumentException("resizeTask not allowed on task=" + task); + Slog.w(TAG, "resizeTask not allowed on task=" + task); + return false; } // Reparent the task to the right stack if necessary @@ -3339,7 +3340,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // After reparenting (which only resizes the task to the stack bounds), resize the // task to the actual bounds provided - task.resize(bounds, resizeMode, preserveWindow); + return task.resize(bounds, resizeMode, preserveWindow); } } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 345cfb0aad71..a45a15ba2012 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -66,6 +66,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { final int mFeatureId; private final DisplayAreaOrganizerController mOrganizerController; IDisplayAreaOrganizer mOrganizer; + private final Configuration mTmpConfiguration = new Configuration(); DisplayArea(WindowManagerService wms, Type type, String name) { this(wms, type, name, FEATURE_UNDEFINED); @@ -162,8 +163,10 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { @Override public void onConfigurationChanged(Configuration newParentConfig) { + mTmpConfiguration.setTo(getConfiguration()); super.onConfigurationChanged(newParentConfig); - if (mOrganizer != null) { + + if (mOrganizer != null && getConfiguration().diff(mTmpConfiguration) != 0) { mOrganizerController.onDisplayAreaInfoChanged(mOrganizer, this); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 0b2bd811bb84..0efb9698f4b0 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -498,8 +498,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ ActivityRecord mFixedRotationLaunchingApp; - FixedRotationAnimationController mFixedRotationAnimationController; - final FixedRotationTransitionListener mFixedRotationTransitionListener = new FixedRotationTransitionListener(); @@ -1540,11 +1538,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } private void startFixedRotationTransform(WindowToken token, int rotation) { - if (mFixedRotationAnimationController == null) { - mFixedRotationAnimationController = new FixedRotationAnimationController( - this); - } - mFixedRotationAnimationController.hide(rotation); mTmpConfiguration.unset(); final DisplayInfo info = computeScreenConfiguration(mTmpConfiguration, rotation); final WmDisplayCutout cutout = calculateDisplayCutoutForRotation(rotation); @@ -1566,13 +1559,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - void finishFixedRotationAnimation() { - if (mFixedRotationAnimationController != null - && mFixedRotationAnimationController.show()) { - mFixedRotationAnimationController = null; - } - } - /** * Update rotation of the display. * @@ -4700,9 +4686,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo boolean supportsSystemDecorations() { return (mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this) || (mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0 - || (mWmService.mForceDesktopModeOnExternalDisplays && !isUntrustedVirtualDisplay())) + || mWmService.mForceDesktopModeOnExternalDisplays) // VR virtual display will be used to run and render 2D app within a VR experience. - && mDisplayId != mWmService.mVr2dDisplayId; + && mDisplayId != mWmService.mVr2dDisplayId + // Do not show system decorations on untrusted virtual display. + && !isUntrustedVirtualDisplay(); } /** diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index c3f906135a00..ebfe70c0c371 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -560,7 +560,6 @@ public class DisplayRotation { }, true /* traverseTopToBottom */); mSeamlessRotationCount = 0; mRotatingSeamlessly = false; - mDisplayContent.finishFixedRotationAnimation(); } private void prepareSeamlessRotation() { @@ -647,7 +646,6 @@ public class DisplayRotation { "Performing post-rotate rotation after seamless rotation"); // Finish seamless rotation. mRotatingSeamlessly = false; - mDisplayContent.finishFixedRotationAnimation(); updateRotationAndSendNewConfigIfChanged(); } diff --git a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java deleted file mode 100644 index 7aca63774889..000000000000 --- a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static com.android.server.wm.AnimationSpecProto.WINDOW; -import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM; -import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION; - -import android.content.res.Configuration; -import android.util.proto.ProtoOutputStream; -import android.view.SurfaceControl; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.view.animation.Transformation; - -import com.android.internal.R; - -import java.io.PrintWriter; -import java.util.ArrayList; - -/** - * Controller to fade out and in system ui when applying a fixed rotation transform to a window - * token. - * - * The system bars will be fade out when the fixed rotation transform starts and will be fade in - * once all surfaces have been rotated. - */ -public class FixedRotationAnimationController { - - private final WindowManagerService mWmService; - private boolean mShowRequested = true; - private int mTargetRotation = Configuration.ORIENTATION_UNDEFINED; - private final ArrayList<WindowState> mAnimatedWindowStates = new ArrayList<>(2); - private final Runnable[] mDeferredFinishCallbacks; - - public FixedRotationAnimationController(DisplayContent displayContent) { - mWmService = displayContent.mWmService; - addAnimatedWindow(displayContent.getDisplayPolicy().getStatusBar()); - addAnimatedWindow(displayContent.getDisplayPolicy().getNavigationBar()); - mDeferredFinishCallbacks = new Runnable[mAnimatedWindowStates.size()]; - } - - private void addAnimatedWindow(WindowState windowState) { - if (windowState != null) { - mAnimatedWindowStates.add(windowState); - } - } - - /** - * Show the previously hidden {@link WindowToken} if their surfaces have already been rotated. - * - * @return True if the show animation has been started, in which case the caller no longer needs - * this {@link FixedRotationAnimationController}. - */ - boolean show() { - if (!mShowRequested && readyToShow()) { - mShowRequested = true; - for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) { - WindowState windowState = mAnimatedWindowStates.get(i); - fadeWindowToken(true, windowState.getParent(), i); - } - return true; - } - return false; - } - - void hide(int targetRotation) { - mTargetRotation = targetRotation; - if (mShowRequested) { - mShowRequested = false; - for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) { - WindowState windowState = mAnimatedWindowStates.get(i); - fadeWindowToken(false /* show */, windowState.getParent(), i); - } - } - } - - void cancel() { - for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) { - WindowState windowState = mAnimatedWindowStates.get(i); - mShowRequested = true; - fadeWindowToken(true /* show */, windowState.getParent(), i); - } - } - - private void fadeWindowToken(boolean show, WindowContainer<WindowToken> windowToken, - int index) { - Animation animation = AnimationUtils.loadAnimation(mWmService.mContext, - show ? R.anim.fade_in : R.anim.fade_out); - LocalAnimationAdapter.AnimationSpec windowAnimationSpec = createAnimationSpec(animation); - - FixedRotationAnimationAdapter animationAdapter = new FixedRotationAnimationAdapter( - windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, index); - - // We deferred the end of the animation when hiding the token, so we need to end it now that - // it's shown again. - SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> { - if (mDeferredFinishCallbacks[index] != null) { - mDeferredFinishCallbacks[index].run(); - mDeferredFinishCallbacks[index] = null; - } - } : null; - windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter, - mShowRequested, ANIMATION_TYPE_FIXED_TRANSFORM, finishedCallback); - } - - /** - * Check if all the mAnimatedWindowState's surfaces have been rotated to the - * mTargetRotation. - */ - private boolean readyToShow() { - for (int i = mAnimatedWindowStates.size() - 1; i >= 0; i--) { - WindowState windowState = mAnimatedWindowStates.get(i); - if (windowState.getConfiguration().windowConfiguration.getRotation() - != mTargetRotation || windowState.mPendingSeamlessRotate != null) { - return false; - } - } - return true; - } - - - private LocalAnimationAdapter.AnimationSpec createAnimationSpec(Animation animation) { - return new LocalAnimationAdapter.AnimationSpec() { - - Transformation mTransformation = new Transformation(); - - @Override - public boolean getShowWallpaper() { - return true; - } - - @Override - public long getDuration() { - return animation.getDuration(); - } - - @Override - public void apply(SurfaceControl.Transaction t, SurfaceControl leash, - long currentPlayTime) { - mTransformation.clear(); - animation.getTransformation(currentPlayTime, mTransformation); - t.setAlpha(leash, mTransformation.getAlpha()); - } - - @Override - public void dump(PrintWriter pw, String prefix) { - pw.print(prefix); - pw.println(animation); - } - - @Override - public void dumpDebugInner(ProtoOutputStream proto) { - final long token = proto.start(WINDOW); - proto.write(ANIMATION, animation.toString()); - proto.end(token); - } - }; - } - - private class FixedRotationAnimationAdapter extends LocalAnimationAdapter { - private final boolean mShow; - private final int mIndex; - - FixedRotationAnimationAdapter(AnimationSpec windowAnimationSpec, - SurfaceAnimationRunner surfaceAnimationRunner, boolean show, int index) { - super(windowAnimationSpec, surfaceAnimationRunner); - mShow = show; - mIndex = index; - } - - @Override - public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { - // We defer the end of the hide animation to ensure the tokens stay hidden until - // we show them again. - if (!mShow) { - mDeferredFinishCallbacks[mIndex] = endDeferFinishCallback; - return true; - } - return false; - } - } -} diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 888a6e986e88..0ecde72cc566 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1369,7 +1369,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> calculateDefaultMinimalSizeOfResizeableTasks(); final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea(); - defaultTaskDisplayArea.getOrCreateRootHomeTask(); + defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP); positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent, false /* includingParents */); } diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 0143eb1abe03..42342a60ba16 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -489,12 +489,6 @@ class SurfaceAnimator { static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5; /** - * Animation when a fixed rotation transform is applied to a window token. - * @hide - */ - static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6; - - /** * Bitmask to include all animation types. This is NOT an {@link AnimationType} * @hide */ @@ -511,8 +505,7 @@ class SurfaceAnimator { ANIMATION_TYPE_DIMMER, ANIMATION_TYPE_RECENTS, ANIMATION_TYPE_WINDOW_ANIMATION, - ANIMATION_TYPE_INSETS_CONTROL, - ANIMATION_TYPE_FIXED_TRANSFORM + ANIMATION_TYPE_INSETS_CONTROL }) @Retention(RetentionPolicy.SOURCE) @interface AnimationType {} diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 37a4c1f6849b..6ce36f1a3eb6 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1461,16 +1461,23 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { return mChildren.get(index); } + @Nullable + ActivityStack getOrCreateRootHomeTask() { + return getOrCreateRootHomeTask(false /* onTop */); + } + /** * Returns the existing home stack or creates and returns a new one if it should exist for the * display. + * @param onTop Only be used when there is no existing home stack. If true the home stack will + * be created at the top of the display, else at the bottom. */ @Nullable - ActivityStack getOrCreateRootHomeTask() { + ActivityStack getOrCreateRootHomeTask(boolean onTop) { ActivityStack homeTask = getRootHomeTask(); if (homeTask == null && mDisplayContent.supportsSystemDecorations() && !mDisplayContent.isUntrustedVirtualDisplay()) { - homeTask = createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, false /* onTop */); + homeTask = createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop); } return homeTask; } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java index 45023acf4466..c6e1c954be12 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java @@ -28,12 +28,14 @@ import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.os.Process; import android.os.SystemClock; +import android.os.UserManagerInternal; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.LocalServices; import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto; import java.io.File; @@ -72,6 +74,7 @@ class TaskSnapshotPersister { private final float mLowResScaleFactor; private boolean mEnableLowResSnapshots; private final boolean mUse16BitFormat; + private final UserManagerInternal mUserManagerInternal; /** * The list of ids of the tasks that have been persisted since {@link #removeObsoleteFiles} was @@ -82,6 +85,8 @@ class TaskSnapshotPersister { TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) { mDirectoryResolver = resolver; + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); + final float highResTaskSnapshotScale = service.mContext.getResources().getFloat( com.android.internal.R.dimen.config_highResTaskSnapshotScale); final float lowResTaskSnapshotScale = service.mContext.getResources().getFloat( @@ -191,7 +196,7 @@ class TaskSnapshotPersister { return; } } - SystemClock.sleep(100); + SystemClock.sleep(DELAY_MS); } } @@ -233,7 +238,7 @@ class TaskSnapshotPersister { private boolean createDirectory(int userId) { final File dir = getDirectory(userId); - return dir.exists() || dir.mkdirs(); + return dir.exists() || dir.mkdir(); } private void deleteSnapshot(int taskId, int userId) { @@ -258,18 +263,26 @@ class TaskSnapshotPersister { android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); while (true) { WriteQueueItem next; + boolean isReadyToWrite = false; synchronized (mLock) { if (mPaused) { next = null; } else { next = mWriteQueue.poll(); if (next != null) { - next.onDequeuedLocked(); + if (next.isReady()) { + isReadyToWrite = true; + next.onDequeuedLocked(); + } else { + mWriteQueue.addLast(next); + } } } } if (next != null) { - next.write(); + if (isReadyToWrite) { + next.write(); + } SystemClock.sleep(DELAY_MS); } synchronized (mLock) { @@ -289,6 +302,13 @@ class TaskSnapshotPersister { }; private abstract class WriteQueueItem { + /** + * @return {@code true} if item is ready to have {@link WriteQueueItem#write} called + */ + boolean isReady() { + return true; + } + abstract void write(); /** @@ -328,6 +348,11 @@ class TaskSnapshotPersister { } @Override + boolean isReady() { + return mUserManagerInternal.isUserUnlocked(mUserId); + } + + @Override void write() { if (!createDirectory(mUserId)) { Slog.e(TAG, "Unable to create snapshot directory for user dir=" diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 7bfddd79f8a4..e2023aeac3dc 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1415,11 +1415,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom, - WindowContainer boundary) { + ActivityRecord boundary) { if (traverseTopToBottom) { for (int i = mChildren.size() - 1; i >= 0; --i) { final WindowContainer wc = mChildren.get(i); - if (wc == boundary) return null; + // TODO(b/156986561): Improve the correctness of the boundary check. + if (wc == boundary) return boundary; final ActivityRecord r = wc.getActivity(callback, traverseTopToBottom, boundary); if (r != null) { @@ -1430,7 +1431,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< final int count = mChildren.size(); for (int i = 0; i < count; i++) { final WindowContainer wc = mChildren.get(i); - if (wc == boundary) return null; + // TODO(b/156986561): Improve the correctness of the boundary check. + if (wc == boundary) return boundary; final ActivityRecord r = wc.getActivity(callback, traverseTopToBottom, boundary); if (r != null) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 8739bad4398b..768f89eff774 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -642,9 +642,6 @@ class WindowToken extends WindowContainer<WindowState> { final int originalRotation = getWindowConfiguration().getRotation(); onConfigurationChanged(parent.getConfiguration()); onCancelFixedRotationTransform(originalRotation); - if (mDisplayContent.mFixedRotationAnimationController != null) { - mDisplayContent.mFixedRotationAnimationController.cancel(); - } } /** diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 78439dba2724..f0dca772adaa 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -63,6 +63,7 @@ struct Constants { static constexpr auto libDir = "lib"sv; static constexpr auto libSuffix = ".so"sv; static constexpr auto blockSize = 4096; + static constexpr auto systemPackage = "android"sv; }; static const Constants& constants() { @@ -377,7 +378,8 @@ void IncrementalService::onSystemReady() { std::lock_guard l(mLock); mounts.reserve(mMounts.size()); for (auto&& [id, ifs] : mMounts) { - if (ifs->mountId == id) { + if (ifs->mountId == id && + ifs->dataLoaderStub->params().packageName == Constants::systemPackage) { mounts.push_back(ifs); } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java index e5ec1f76c554..96a44a46bbaf 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java @@ -16,14 +16,23 @@ package com.android.server.am; +import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; + import static com.android.server.am.ActivityManagerService.Injector; import static com.android.server.am.CachedAppOptimizer.compactActionIntToString; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.content.ComponentName; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManagerInternal; import android.os.Handler; import android.os.HandlerThread; +import android.os.MessageQueue; import android.os.Process; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; @@ -31,9 +40,11 @@ import android.text.TextUtils; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.appop.AppOpsService; import com.android.server.testables.TestableDeviceConfig; +import com.android.server.wm.ActivityTaskManagerService; import org.junit.After; import org.junit.Assume; @@ -45,6 +56,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.io.File; +import java.io.IOException; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -68,25 +80,36 @@ public final class CachedAppOptimizerTest { private HandlerThread mHandlerThread; private Handler mHandler; private CountDownLatch mCountDown; + private ActivityManagerService mAms; + private Context mContext; + private TestInjector mInjector; + private TestProcessDependencies mProcessDependencies; + + @Mock + private PackageManagerInternal mPackageManagerInt; @Rule - public TestableDeviceConfig.TestableDeviceConfigRule + public final TestableDeviceConfig.TestableDeviceConfigRule mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); + @Rule + public final ApplicationExitInfoTest.ServiceThreadRule + mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule(); @Before public void setUp() { mHandlerThread = new HandlerThread(""); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); - mThread = new ServiceThread("TestServiceThread", Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */); mThread.start(); - - ActivityManagerService ams = new ActivityManagerService( - new TestInjector(InstrumentationRegistry.getInstrumentation().getContext()), - mThread); - mCachedAppOptimizerUnderTest = new CachedAppOptimizer(ams, + mContext = InstrumentationRegistry.getInstrumentation().getContext(); + mInjector = new TestInjector(mContext); + mAms = new ActivityManagerService( + new TestInjector(mContext), mServiceThreadRule.getThread()); + doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent(); + mProcessDependencies = new TestProcessDependencies(); + mCachedAppOptimizerUnderTest = new CachedAppOptimizer(mAms, new CachedAppOptimizer.PropertyChangedCallbackForTest() { @Override public void onPropertyChanged() { @@ -94,7 +117,9 @@ public final class CachedAppOptimizerTest { mCountDown.countDown(); } } - }); + }, mProcessDependencies); + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); } @After @@ -104,6 +129,19 @@ public final class CachedAppOptimizerTest { mCountDown = null; } + private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, String processName, + String packageName) { + ApplicationInfo ai = new ApplicationInfo(); + ai.packageName = packageName; + ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid); + app.pid = pid; + app.info.uid = packageUid; + // Exact value does not mater, it can be any state for which compaction is allowed. + app.setProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE; + app.setAdj = 905; + return app; + } + @Test public void init_setsDefaults() { mCachedAppOptimizerUnderTest.init(); @@ -197,7 +235,7 @@ public final class CachedAppOptimizerTest { CachedAppOptimizer.DEFAULT_USE_FREEZER); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, CachedAppOptimizer.KEY_USE_FREEZER, CachedAppOptimizer.DEFAULT_USE_FREEZER - ? "false" : "true" , false); + ? "false" : "true", false); // Then calling init will read and set that flag. mCachedAppOptimizerUnderTest.init(); @@ -790,6 +828,174 @@ public final class CachedAppOptimizerTest { .containsExactlyElementsIn(expected); } + @Test + public void processWithDeltaRSSTooSmall_notFullCompacted() throws Exception { + // Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set RSS + // throttle to 12000. + mCachedAppOptimizerUnderTest.init(); + setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true); + setFlag(CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "12000", false); + initActivityManagerService(); + + // Simulate RSS anon memory larger than throttle. + long[] rssBefore1 = + new long[]{/*totalRSS*/ 10000, /*fileRSS*/ 10000, /*anonRSS*/ 12000, /*swap*/ + 10000}; + long[] rssAfter1 = + new long[]{/*totalRSS*/ 9000, /*fileRSS*/ 9000, /*anonRSS*/ 11000, /*swap*/9000}; + // Delta between rssAfter1 and rssBefore2 is below threshold (500). + long[] rssBefore2 = + new long[]{/*totalRSS*/ 9500, /*fileRSS*/ 9500, /*anonRSS*/ 11500, /*swap*/9500}; + long[] rssAfter2 = + new long[]{/*totalRSS*/ 8000, /*fileRSS*/ 8000, /*anonRSS*/ 9000, /*swap*/8000}; + // Delta between rssAfter1 and rssBefore3 is above threshold (13000). + long[] rssBefore3 = + new long[]{/*totalRSS*/ 10000, /*fileRSS*/ 18000, /*anonRSS*/ 13000, /*swap*/ 7000}; + long[] rssAfter3 = + new long[]{/*totalRSS*/ 10000, /*fileRSS*/ 11000, /*anonRSS*/ 10000, /*swap*/ 6000}; + long[] valuesAfter = {}; + // Process that passes properties. + int pid = 1; + ProcessRecord processRecord = makeProcessRecord(pid, 2, 3, "p1", "app1"); + + // GIVEN we simulate RSS memory before above thresholds and it is the first time 'p1' is + // compacted. + mProcessDependencies.setRss(rssBefore1); + mProcessDependencies.setRssAfterCompaction(rssAfter1); // + // WHEN we try to run compaction + mCachedAppOptimizerUnderTest.compactAppFull(processRecord); + waitForHandler(); + // THEN process IS compacted. + assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull(); + valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get( + pid).getRssAfterCompaction(); + assertThat(valuesAfter).isEqualTo(rssAfter1); + + // WHEN delta is below threshold (500). + mProcessDependencies.setRss(rssBefore2); + mProcessDependencies.setRssAfterCompaction(rssAfter2); + // This is to avoid throttle of compacting too soon. + processRecord.lastCompactTime = processRecord.lastCompactTime - 10_000; + // WHEN we try to run compaction. + mCachedAppOptimizerUnderTest.compactAppFull(processRecord); + waitForHandler(); + // THEN process IS NOT compacted - values after compaction for process 1 should remain the + // same as from the last compaction. + assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull(); + valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get( + pid).getRssAfterCompaction(); + assertThat(valuesAfter).isEqualTo(rssAfter1); + + // WHEN delta is above threshold (13000). + mProcessDependencies.setRss(rssBefore3); + mProcessDependencies.setRssAfterCompaction(rssAfter3); + // This is to avoid throttle of compacting too soon. + processRecord.lastCompactTime = processRecord.lastCompactTime - 10_000; + // WHEN we try to run compaction + mCachedAppOptimizerUnderTest.compactAppFull(processRecord); + waitForHandler(); + // THEN process IS compacted - values after compaction for process 1 should be updated. + assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull(); + valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get( + pid).getRssAfterCompaction(); + assertThat(valuesAfter).isEqualTo(rssAfter3); + + } + + @Test + public void processWithAnonRSSTooSmall_notFullCompacted() throws Exception { + // Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set RSS + // throttle to 8000. + mCachedAppOptimizerUnderTest.init(); + setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true); + setFlag(CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "8000", false); + initActivityManagerService(); + + // Simulate RSS anon memory larger than throttle. + long[] rssBelowThreshold = + new long[]{/*Total RSS*/ 10000, /*File RSS*/ 10000, /*Anon RSS*/ 7000, /*Swap*/ + 10000}; + long[] rssBelowThresholdAfter = + new long[]{/*Total RSS*/ 9000, /*File RSS*/ 7000, /*Anon RSS*/ 4000, /*Swap*/ + 8000}; + long[] rssAboveThreshold = + new long[]{/*Total RSS*/ 10000, /*File RSS*/ 10000, /*Anon RSS*/ 9000, /*Swap*/ + 10000}; + long[] rssAboveThresholdAfter = + new long[]{/*Total RSS*/ 8000, /*File RSS*/ 9000, /*Anon RSS*/ 6000, /*Swap*/5000}; + // Process that passes properties. + int pid = 1; + ProcessRecord processRecord = + makeProcessRecord(pid, 2, 3, "p1", + "app1"); + + // GIVEN we simulate RSS memory before below threshold. + mProcessDependencies.setRss(rssBelowThreshold); + mProcessDependencies.setRssAfterCompaction(rssBelowThresholdAfter); + // WHEN we try to run compaction + mCachedAppOptimizerUnderTest.compactAppFull(processRecord); + waitForHandler(); + // THEN process IS NOT compacted. + assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull(); + + // GIVEN we simulate RSS memory before above threshold. + mProcessDependencies.setRss(rssAboveThreshold); + mProcessDependencies.setRssAfterCompaction(rssAboveThresholdAfter); + // WHEN we try to run compaction + mCachedAppOptimizerUnderTest.compactAppFull(processRecord); + waitForHandler(); + // THEN process IS compacted. + assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull(); + long[] valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get( + pid).getRssAfterCompaction(); + assertThat(valuesAfter).isEqualTo(rssAboveThresholdAfter); + } + + + private void setFlag(String key, String value, boolean defaultValue) throws Exception { + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, key, value, defaultValue); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + } + + private void waitForHandler() { + Idle idle = new Idle(); + mCachedAppOptimizerUnderTest.mCompactionHandler.getLooper().getQueue().addIdleHandler(idle); + mCachedAppOptimizerUnderTest.mCompactionHandler.post(() -> { }); + idle.waitForIdle(); + } + + private void initActivityManagerService() { + mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread()); + mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext); + mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper()); + mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal()); + mAms.mPackageManagerInt = mPackageManagerInt; + } + + private static final class Idle implements MessageQueue.IdleHandler { + private boolean mIdle; + + @Override + public boolean queueIdle() { + synchronized (this) { + mIdle = true; + notifyAll(); + } + return false; + } + + public synchronized void waitForIdle() { + while (!mIdle) { + try { + // Wait with a timeout of 10s. + wait(10000); + } catch (InterruptedException e) { + } + } + } + } + private class TestInjector extends Injector { TestInjector(Context context) { @@ -806,4 +1012,29 @@ public final class CachedAppOptimizerTest { return mHandler; } } + + // Test implementation for ProcessDependencies. + private static final class TestProcessDependencies + implements CachedAppOptimizer.ProcessDependencies { + private long[] mRss; + private long[] mRssAfterCompaction; + + @Override + public long[] getRss(int pid) { + return mRss; + } + + @Override + public void performCompaction(String action, int pid) throws IOException { + mRss = mRssAfterCompaction; + } + + public void setRss(long[] newValues) { + mRss = newValues; + } + + public void setRssAfterCompaction(long[] newValues) { + mRssAfterCompaction = newValues; + } + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index efa25bd7721b..320dacff4888 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -1582,6 +1582,41 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { "s2"); } + public void testCachedShortcuts_canPassShortcutLimit() { + // Change the max number of shortcuts. + mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=4"); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertTrue(mManager.setDynamicShortcuts(list(makeLongLivedShortcut("s1"), + makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"), + makeLongLivedShortcut("s4")))); + }); + + // Cache All + runWithCaller(LAUNCHER_1, USER_0, () -> { + mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3", "s4"), + HANDLE_USER_0); + }); + + setCaller(CALLING_PACKAGE_1); + + // Get dynamic shortcuts + assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC), + "s1", "s2", "s3", "s4"); + // Get cached shortcuts + assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), + "s1", "s2", "s3", "s4"); + + assertTrue(mManager.setDynamicShortcuts(makeShortcuts("sx1", "sx2", "sx3", "sx4"))); + + // Get dynamic shortcuts + assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC), + "sx1", "sx2", "sx3", "sx4"); + // Get cached shortcuts + assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), + "s1", "s2", "s3", "s4"); + } + // === Test for launcher side APIs === public void testGetShortcuts() { diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index e4102205ddbb..2d45f9ea40c7 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -387,7 +387,7 @@ public class AppStandbyControllerTests { @Test public void testBoundWidgetPackageExempt() throws Exception { assumeTrue(mInjector.getContext().getSystemService(AppWidgetManager.class) != null); - assertEquals(STANDBY_BUCKET_EXEMPTED, + assertEquals(STANDBY_BUCKET_ACTIVE, mController.getAppStandbyBucket(PACKAGE_EXEMPTED_1, USER_ID, mInjector.mElapsedRealtime, false)); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index f4e5d569512a..078c21e04512 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -81,6 +81,7 @@ import android.testing.TestableContentResolver; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Pair; +import android.util.StatsEvent; import android.util.Xml; import androidx.test.InstrumentationRegistry; @@ -89,6 +90,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.FastXmlSerializer; import com.android.server.UiServiceTestCase; + import org.json.JSONArray; import org.json.JSONObject; import org.junit.Before; @@ -2996,6 +2998,31 @@ public class PreferencesHelperTest extends UiServiceTestCase { PKG_O, UID_O, parent.getId(), conversationId, false, false), conversationId); } + + @Test + public void testPullConversationNotificationChannel() { + String conversationId = "friend"; + + NotificationChannel parent = + new NotificationChannel("parent", "messages", IMPORTANCE_DEFAULT); + mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false); + + String channelId = String.format( + CONVERSATION_CHANNEL_ID_FORMAT, parent.getId(), conversationId); + NotificationChannel friend = new NotificationChannel(channelId, + "messages", IMPORTANCE_DEFAULT); + friend.setConversationId(parent.getId(), conversationId); + mHelper.createNotificationChannel(PKG_O, UID_O, friend, true, false); + ArrayList<StatsEvent> events = new ArrayList<>(); + mHelper.pullPackageChannelPreferencesStats(events); + boolean found = false; + for (StatsEvent event : events) { + // TODO(b/153195691): inspect the content once it is possible to do so + found = true; + } + assertTrue("conversation was not in the pull", found); + } + @Test public void testGetNotificationChannel_conversationProvidedByNotCustomizedYet() { String conversationId = "friend"; diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java index 6a1f50d7e58a..f4b50dc6b553 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; import android.os.RemoteException; @@ -91,5 +92,11 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { mDisplayContent.setBounds(new Rect(0, 0, 1000, 1000)); verify(organizer).onDisplayAreaInfoChanged(any()); + + Configuration tmpConfiguration = new Configuration(); + tmpConfiguration.setTo(mDisplayContent.getRequestedOverrideConfiguration()); + mDisplayContent.onRequestedOverrideConfigurationChanged(tmpConfiguration); + // Ensure it was still only called once if the bounds didn't change + verify(organizer).onDisplayAreaInfoChanged(any()); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 7b23bfb48a1a..ac95a817bec9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -57,7 +57,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.same; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; @@ -1060,8 +1059,6 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testApplyTopFixedRotationTransform() { mWm.mIsFixedRotationTransformEnabled = true; - mDisplayContent.getDisplayPolicy().addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs); - mDisplayContent.getDisplayPolicy().addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs); final Configuration config90 = new Configuration(); mDisplayContent.computeScreenConfiguration(config90, ROTATION_90); @@ -1082,12 +1079,6 @@ public class DisplayContentTests extends WindowTestsBase { ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */, false /* forceUpdate */)); - assertNotNull(mDisplayContent.mFixedRotationAnimationController); - assertTrue(mStatusBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS, - ANIMATION_TYPE_FIXED_TRANSFORM)); - assertTrue(mNavBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS, - ANIMATION_TYPE_FIXED_TRANSFORM)); - final Rect outFrame = new Rect(); final Rect outInsets = new Rect(); final Rect outStableInsets = new Rect(); @@ -1140,9 +1131,6 @@ public class DisplayContentTests extends WindowTestsBase { assertFalse(app.hasFixedRotationTransform()); assertFalse(app2.hasFixedRotationTransform()); assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation); - - mDisplayContent.finishFixedRotationAnimation(); - assertNull(mDisplayContent.mFixedRotationAnimationController); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java index 88de34dd36b0..bdcae481b378 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java @@ -24,6 +24,7 @@ import static android.graphics.GraphicBuffer.USAGE_SW_READ_RARELY; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -39,10 +40,15 @@ import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.os.UserManager; +import android.os.UserManagerInternal; import android.view.Surface; +import com.android.server.LocalServices; + import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import java.io.File; import java.util.function.Predicate; @@ -70,11 +76,26 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { mLowResScale = lowResScale; } + @BeforeClass + public static void setUpOnce() { + final UserManagerInternal userManager = mock(UserManagerInternal.class); + LocalServices.addService(UserManagerInternal.class, userManager); + } + + @AfterClass + public static void tearDownOnce() { + LocalServices.removeServiceForTest(UserManagerInternal.class); + } + @Before public void setUp() { final UserManager um = UserManager.get(getInstrumentation().getTargetContext()); mTestUserId = um.getUserHandle(); + final UserManagerInternal userManagerInternal = + LocalServices.getService(UserManagerInternal.class); + when(userManagerInternal.isUserUnlocked(mTestUserId)).thenReturn(true); + mContextSpy = spy(new ContextWrapper(mWm.mContext)); mResourcesSpy = spy(mContextSpy.getResources()); when(mContextSpy.getResources()).thenReturn(mResourcesSpy); diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index 42e2bbf08834..6c13cd799bc2 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -346,12 +346,15 @@ public class SoundTriggerService extends SystemService { sEventLogger.log(new SoundTriggerLogger.StringEvent("deleteSoundModel(): id = " + soundModelId)); - // Unload the model if it is loaded. - mSoundTriggerHelper.unloadGenericSoundModel(soundModelId.getUuid()); - mDbHelper.deleteGenericSoundModel(soundModelId.getUuid()); + if (isInitialized()) { + // Unload the model if it is loaded. + mSoundTriggerHelper.unloadGenericSoundModel(soundModelId.getUuid()); - // Stop recognition if it is started. - mSoundModelStatTracker.onStop(soundModelId.getUuid()); + // Stop tracking recognition if it is started. + mSoundModelStatTracker.onStop(soundModelId.getUuid()); + } + + mDbHelper.deleteGenericSoundModel(soundModelId.getUuid()); } @Override diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java index bb6f154335a9..b35b3236afc6 100644 --- a/telephony/common/com/android/internal/telephony/SmsApplication.java +++ b/telephony/common/com/android/internal/telephony/SmsApplication.java @@ -536,13 +536,16 @@ public final class SmsApplication { // Assign permission to special system apps assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps, - PHONE_PACKAGE_NAME); + PHONE_PACKAGE_NAME, true); assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps, - BLUETOOTH_PACKAGE_NAME); + BLUETOOTH_PACKAGE_NAME, true); assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps, - MMS_SERVICE_PACKAGE_NAME); + MMS_SERVICE_PACKAGE_NAME, true); assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps, - TELEPHONY_PROVIDER_PACKAGE_NAME); + TELEPHONY_PROVIDER_PACKAGE_NAME, true); + // CellbroadcastReceiver is a mainline module thus skip signature match. + assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps, + CellBroadcastUtils.getDefaultCellBroadcastReceiverPackageName(context), false); // Give AppOps permission to UID 1001 which contains multiple // apps, all of them should be able to write to telephony provider. @@ -744,17 +747,23 @@ public final class SmsApplication { * @param packageManager The package manager instance * @param appOps The AppOps manager instance * @param packageName The package name of the system app + * @param sigatureMatch whether to check signature match */ private static void assignExclusiveSmsPermissionsToSystemApp(Context context, - PackageManager packageManager, AppOpsManager appOps, String packageName) { + PackageManager packageManager, AppOpsManager appOps, String packageName, + boolean sigatureMatch) { // First check package signature matches the caller's package signature. // Since this class is only used internally by the system, this check makes sure // the package signature matches system signature. - final int result = packageManager.checkSignatures(context.getPackageName(), packageName); - if (result != PackageManager.SIGNATURE_MATCH) { - Log.e(LOG_TAG, packageName + " does not have system signature"); - return; + if (sigatureMatch) { + final int result = packageManager.checkSignatures(context.getPackageName(), + packageName); + if (result != PackageManager.SIGNATURE_MATCH) { + Log.e(LOG_TAG, packageName + " does not have system signature"); + return; + } } + try { PackageInfo info = packageManager.getPackageInfo(packageName, 0); int mode = appOps.unsafeCheckOp(AppOpsManager.OPSTR_WRITE_SMS, info.applicationInfo.uid, diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 35464340550b..d62cd0a63b44 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -837,20 +837,20 @@ public class SubscriptionInfo implements Parcelable { + " carrierId=" + mCarrierId + " displayName=" + mDisplayName + " carrierName=" + mCarrierName + " nameSource=" + mNameSource + " iconTint=" + mIconTint - + " mNumber=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumber) - + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc - + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded - + " nativeAccessRules " + Arrays.toString(mNativeAccessRules) + + " number=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumber) + + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc=" + mMcc + + " mnc=" + mMnc + " countryIso=" + mCountryIso + " isEmbedded=" + mIsEmbedded + + " nativeAccessRules=" + Arrays.toString(mNativeAccessRules) + " cardString=" + cardStringToPrint + " cardId=" + mCardId - + " isOpportunistic=" + mIsOpportunistic + " mGroupUUID=" + mGroupUUID - + " mIsGroupDisabled=" + mIsGroupDisabled + + " isOpportunistic=" + mIsOpportunistic + " groupUUID=" + mGroupUUID + + " isGroupDisabled=" + mIsGroupDisabled + " profileClass=" + mProfileClass + " ehplmns=" + Arrays.toString(mEhplmns) + " hplmns=" + Arrays.toString(mHplmns) + " subscriptionType=" + mSubscriptionType - + " mGroupOwner=" + mGroupOwner + + " groupOwner=" + mGroupOwner + " carrierConfigAccessRules=" + Arrays.toString(mCarrierConfigAccessRules) - + " mAreUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled + "}"; + + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled + "}"; } @Override diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index ede67dd9fd61..94407f1dcd3a 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -56,14 +56,15 @@ public class ImsRcsManager { * Activity Action: Show the opt-in dialog for enabling or disabling RCS contact discovery * using User Capability Exchange (UCE). * <p> - * An application that depends on contact discovery being enabled may send this intent + * An application that depends on RCS contact discovery being enabled must send this intent * using {@link Context#startActivity(Intent)} to ask the user to opt-in for contacts upload for - * capability exchange if it is currently disabled. Whether or not this setting has been enabled - * can be queried using {@link RcsUceAdapter#isUceSettingEnabled()}. + * capability exchange if it is currently disabled. Whether or not RCS contact discovery has + * been enabled by the user can be queried using {@link RcsUceAdapter#isUceSettingEnabled()}. * <p> - * This intent should only be sent if the carrier supports RCS capability exchange, which can be - * queried using the key {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL}. Otherwise, the - * setting will not be present. + * This intent will always be handled by the system, however the application should only send + * this Intent if the carrier supports RCS contact discovery, which can be queried using the key + * {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL}. Otherwise, the RCS contact discovery + * opt-in dialog will not be shown. * <p> * Input: A mandatory {@link Settings#EXTRA_SUB_ID} extra containing the subscription that the * setting will be be shown for. diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 05ab6bd75878..ec112790a144 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -363,9 +363,10 @@ public class RcsUceAdapter { /** * Change the user’s setting for whether or not UCE is enabled for the associated subscription. * <p> - * If an application Requires UCE, they may launch an Activity using the Intent + * If an application Requires UCE, they will launch an Activity using the Intent * {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}, which will ask the user if - * they wish to enable this feature. + * they wish to enable this feature. This setting should only be enabled after the user has + * opted-in to capability exchange. * <p> * Note: This setting does not affect whether or not the device publishes its service * capabilities if the subscription supports presence publication. |