diff options
324 files changed, 6181 insertions, 2188 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/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index ffd83ba978f4..7090bd46635d 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -307,9 +307,6 @@ private: FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation); FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition); FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation); FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket); FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets); @@ -328,6 +325,7 @@ private: FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation); FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations); + FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges); FRIEND_TEST(CountMetricE2eTest, TestSlicedState); FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap); FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates); @@ -345,6 +343,10 @@ private: FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat); FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset); + FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges); + FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents); + FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm); + FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation); FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index f00a35d10819..573a84f2fe33 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,60 @@ message MobileBytesTransferByFgBg { } /** + * Used for pull network statistics via mobile|wifi networks, and sliced by interesting dimensions. + * Note that the data is expected to be sliced into more dimensions in future. In other words, + * the caller must not assume any row of data is one full report when filtering with a set of + * matching conditions, because future data may represent with multiple rows what is currently + * represented by one. + * To avoid being broken by future slicing, callers must take care to aggregate rows even if they + * query all the existing columns. + * + * 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 across subscriptions. + optional string sim_mcc = 7; + optional string sim_mnc = 8; + + // Allows mobile virtual network operators (MVNOs) to be identified with individual IDs. + // See TelephonyManager#getSimCarrierId. + optional int32 carrier_id = 9; + + // Enumeration of opportunistic states with an additional ALL state indicates the record is + // combined regardless of the boolean value in its field. + enum DataSubscriptionState { + UNKNOWN = 0; // For server side backward compatibility. + ALL = 1; + OPPORTUNISTIC = 2; + NOT_OPPORTUNISTIC = 3; + } + // Mark whether the subscription is an opportunistic data subscription, and ALL indicates the + // record is combined across opportunistic data subscriptions. + // See {@link SubscriptionManager#setOpportunistic}. + optional DataSubscriptionState opportunistic_data_sub = 10; +} + +/** * Pulls bytes transferred via bluetooth. It is pulled from Bluetooth controller. * * Pulled from: @@ -5982,6 +6040,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 +9185,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 +9842,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..e86fdf06e836 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. @@ -459,6 +459,7 @@ protected: FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap); FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates); FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields); + FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges); FRIEND_TEST(DurationMetricE2eTest, TestOneBucket); FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); @@ -488,6 +489,7 @@ protected: FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); + FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index c30532a39244..ad30a88c5d19 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -292,9 +292,6 @@ private: FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation); FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition); FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation); FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket); FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets); @@ -322,6 +319,7 @@ private: TestActivationOnBootMultipleActivationsDifferentActivationTypes); FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); + FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges); FRIEND_TEST(CountMetricE2eTest, TestSlicedState); FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap); FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates); @@ -339,6 +337,10 @@ private: FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSuperset); FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset); + FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges); + FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents); + FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm); + FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation); FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); 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/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 7c0057d87ca9..2e6043df0e64 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -131,7 +131,7 @@ message SimplePredicate { UNKNOWN = 0; FALSE = 1; } - optional InitialValue initial_value = 5 [default = FALSE]; + optional InitialValue initial_value = 5 [default = UNKNOWN]; optional FieldMatcher dimensions = 6; } diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp index 1a7cd5584f3d..04eb40080631 100644 --- a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp @@ -28,6 +28,92 @@ namespace statsd { #ifdef __ANDROID__ /** + * Tests the initial condition and condition after the first log events for + * count metrics with either a combination condition or simple condition. + * + * Metrics should be initialized with condition kUnknown (given that the + * predicate is using the default InitialValue of UNKNOWN). The condition should + * be updated to either kFalse or kTrue if a condition event is logged for all + * children conditions. + */ +TEST(CountMetricE2eTest, TestInitialConditionChanges) { + // Initialize config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. + + auto syncStartMatcher = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = syncStartMatcher; + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = CreateBatteryStateNoneMatcher(); + *config.add_atom_matcher() = CreateBatteryStateUsbMatcher(); + + auto screenOnPredicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = screenOnPredicate; + + auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate(); + *config.add_predicate() = deviceUnpluggedPredicate; + + auto screenOnOnBatteryPredicate = config.add_predicate(); + screenOnOnBatteryPredicate->set_id(StringToId("screenOnOnBatteryPredicate")); + screenOnOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND); + addPredicateToPredicateCombination(screenOnPredicate, screenOnOnBatteryPredicate); + addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOnOnBatteryPredicate); + + // CountSyncStartWhileScreenOnOnBattery (CombinationCondition) + CountMetric* countMetric1 = config.add_count_metric(); + countMetric1->set_id(StringToId("CountSyncStartWhileScreenOnOnBattery")); + countMetric1->set_what(syncStartMatcher.id()); + countMetric1->set_condition(screenOnOnBatteryPredicate->id()); + countMetric1->set_bucket(FIVE_MINUTES); + + // CountSyncStartWhileOnBattery (SimpleCondition) + CountMetric* countMetric2 = config.add_count_metric(); + countMetric2->set_id(StringToId("CountSyncStartWhileOnBatterySliceScreen")); + countMetric2->set_what(syncStartMatcher.id()); + countMetric2->set_condition(deviceUnpluggedPredicate.id()); + countMetric2->set_bucket(FIVE_MINUTES); + + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + const uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(2, metricsManager->mAllMetricProducers.size()); + + sp<MetricProducer> metricProducer1 = metricsManager->mAllMetricProducers[0]; + sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[1]; + + EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); + EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); + + auto screenOnEvent = + CreateScreenStateChangedEvent(bucketStartTimeNs + 30, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); + EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); + + auto pluggedUsbEvent = CreateBatteryStateChangedEvent( + bucketStartTimeNs + 50, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); + processor->OnLogEvent(pluggedUsbEvent.get()); + EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition); + EXPECT_EQ(ConditionState::kFalse, metricProducer2->mCondition); + + auto pluggedNoneEvent = CreateBatteryStateChangedEvent( + bucketStartTimeNs + 70, BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE); + processor->OnLogEvent(pluggedNoneEvent.get()); + EXPECT_EQ(ConditionState::kTrue, metricProducer1->mCondition); + EXPECT_EQ(ConditionState::kTrue, metricProducer2->mCondition); +} + +/** * Test a count metric that has one slice_by_state with no primary fields. * * Once the CountMetricProducer is initialized, it has one atom id in @@ -85,7 +171,7 @@ TEST(CountMetricE2eTest, TestSlicedState) { x x x x x x (syncStartEvents) | | (ScreenIsOnEvent) | | (ScreenIsOffEvent) - | (ScreenUnknownEvent) + | (ScreenDozeEvent) */ // Initialize log events - first bucket. std::vector<int> attributionUids1 = {123}; @@ -243,9 +329,8 @@ TEST(CountMetricE2eTest, TestSlicedStateWithMap) { |-----------------------------|-----------------------------|-- x x x x x x x x x (syncStartEvents) -----------------------------------------------------------SCREEN_OFF events - | (ScreenStateUnknownEvent = 0) | | (ScreenStateOffEvent = 1) - | (ScreenStateDozeEvent = 3) + | | (ScreenStateDozeEvent = 3) | (ScreenStateDozeSuspendEvent = 4) -----------------------------------------------------------SCREEN_ON events @@ -262,7 +347,7 @@ TEST(CountMetricE2eTest, TestSlicedStateWithMap) { attributionTags1, "sync_name")); // 0:30 events.push_back(CreateScreenStateChangedEvent( bucketStartTimeNs + 30 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN)); // 0:40 + android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 0:40 events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 60 * NS_PER_SEC, attributionUids1, attributionTags1, "sync_name")); // 1:10 events.push_back(CreateScreenStateChangedEvent( @@ -625,9 +710,8 @@ TEST(CountMetricE2eTest, TestMultipleSlicedStates) { |------------------------|------------------------|-- 1 1 1 1 1 2 1 1 2 (AppCrashEvents) ---------------------------------------------------SCREEN_OFF events - | (ScreenUnknownEvent = 0) | | (ScreenOffEvent = 1) - | (ScreenDozeEvent = 3) + | | (ScreenDozeEvent = 3) ---------------------------------------------------SCREEN_ON events | | (ScreenOnEvent = 2) | (ScreenOnSuspendEvent = 6) @@ -660,7 +744,7 @@ TEST(CountMetricE2eTest, TestMultipleSlicedStates) { CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /*uid*/)); // 0:30 events.push_back(CreateScreenStateChangedEvent( bucketStartTimeNs + 30 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN)); // 0:40 + android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 0:40 events.push_back( CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, 1 /*uid*/)); // 1:10 events.push_back(CreateUidProcessStateChangedEvent( diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp index e595f290ffdf..4d3928277527 100644 --- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp @@ -63,8 +63,143 @@ StatsdConfig CreateStatsdConfig(bool useCondition = true) { return config; } +StatsdConfig CreateStatsdConfigWithStates() { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. + + auto pulledAtomMatcher = CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE); + *config.add_atom_matcher() = pulledAtomMatcher; + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = CreateBatteryStateNoneMatcher(); + *config.add_atom_matcher() = CreateBatteryStateUsbMatcher(); + + auto screenOnPredicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = screenOnPredicate; + + auto screenOffPredicate = CreateScreenIsOffPredicate(); + *config.add_predicate() = screenOffPredicate; + + auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate(); + *config.add_predicate() = deviceUnpluggedPredicate; + + auto screenOnOnBatteryPredicate = config.add_predicate(); + screenOnOnBatteryPredicate->set_id(StringToId("screenOnOnBatteryPredicate")); + screenOnOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND); + addPredicateToPredicateCombination(screenOnPredicate, screenOnOnBatteryPredicate); + addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOnOnBatteryPredicate); + + auto screenOffOnBatteryPredicate = config.add_predicate(); + screenOffOnBatteryPredicate->set_id(StringToId("ScreenOffOnBattery")); + screenOffOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND); + addPredicateToPredicateCombination(screenOffPredicate, screenOffOnBatteryPredicate); + addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOffOnBatteryPredicate); + + const State screenState = + CreateScreenStateWithSimpleOnOffMap(/*screen on id=*/321, /*screen off id=*/123); + *config.add_state() = screenState; + + // ValueMetricSubsystemSleepWhileScreenOnOnBattery + auto valueMetric1 = config.add_value_metric(); + valueMetric1->set_id(metricId); + valueMetric1->set_what(pulledAtomMatcher.id()); + valueMetric1->set_condition(screenOnOnBatteryPredicate->id()); + *valueMetric1->mutable_value_field() = + CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); + valueMetric1->set_bucket(FIVE_MINUTES); + valueMetric1->set_use_absolute_value_on_reset(true); + valueMetric1->set_skip_zero_diff_output(false); + valueMetric1->set_max_pull_delay_sec(INT_MAX); + + // ValueMetricSubsystemSleepWhileScreenOffOnBattery + ValueMetric* valueMetric2 = config.add_value_metric(); + valueMetric2->set_id(StringToId("ValueMetricSubsystemSleepWhileScreenOffOnBattery")); + valueMetric2->set_what(pulledAtomMatcher.id()); + valueMetric2->set_condition(screenOffOnBatteryPredicate->id()); + *valueMetric2->mutable_value_field() = + CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); + valueMetric2->set_bucket(FIVE_MINUTES); + valueMetric2->set_use_absolute_value_on_reset(true); + valueMetric2->set_skip_zero_diff_output(false); + valueMetric2->set_max_pull_delay_sec(INT_MAX); + + // ValueMetricSubsystemSleepWhileOnBatterySliceScreen + ValueMetric* valueMetric3 = config.add_value_metric(); + valueMetric3->set_id(StringToId("ValueMetricSubsystemSleepWhileOnBatterySliceScreen")); + valueMetric3->set_what(pulledAtomMatcher.id()); + valueMetric3->set_condition(deviceUnpluggedPredicate.id()); + *valueMetric3->mutable_value_field() = + CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); + valueMetric3->add_slice_by_state(screenState.id()); + valueMetric3->set_bucket(FIVE_MINUTES); + valueMetric3->set_use_absolute_value_on_reset(true); + valueMetric3->set_skip_zero_diff_output(false); + valueMetric3->set_max_pull_delay_sec(INT_MAX); + return config; +} + } // namespace +/** + * Tests the initial condition and condition after the first log events for + * value metrics with either a combination condition or simple condition. + * + * Metrics should be initialized with condition kUnknown (given that the + * predicate is using the default InitialValue of UNKNOWN). The condition should + * be updated to either kFalse or kTrue if a condition event is logged for all + * children conditions. + */ +TEST(ValueMetricE2eTest, TestInitialConditionChanges) { + StatsdConfig config = CreateStatsdConfigWithStates(); + int64_t baseTimeNs = getElapsedRealtimeNs(); + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + int32_t tagId = util::SUBSYSTEM_SLEEP_STATE; + auto processor = + CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), tagId); + + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(3, metricsManager->mAllMetricProducers.size()); + + // Combination condition metric - screen on and device unplugged + sp<MetricProducer> metricProducer1 = metricsManager->mAllMetricProducers[0]; + // Simple condition metric - device unplugged + sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[2]; + + EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); + EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); + + auto screenOnEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 30, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); + EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); + + auto screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 40, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); + EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); + + auto pluggedUsbEvent = CreateBatteryStateChangedEvent( + configAddedTimeNs + 50, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); + processor->OnLogEvent(pluggedUsbEvent.get()); + EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition); + EXPECT_EQ(ConditionState::kFalse, metricProducer2->mCondition); + + auto pluggedNoneEvent = CreateBatteryStateChangedEvent( + configAddedTimeNs + 70, BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE); + processor->OnLogEvent(pluggedNoneEvent.get()); + EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition); + EXPECT_EQ(ConditionState::kTrue, metricProducer2->mCondition); +} + TEST(ValueMetricE2eTest, TestPulledEvents) { auto config = CreateStatsdConfig(); int64_t baseTimeNs = getElapsedRealtimeNs(); 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/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 6a7ad1faddea..582df0c1a2a3 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -169,7 +169,6 @@ AtomMatcher CreateScreenStateChangedAtomMatcher( return atom_matcher; } - AtomMatcher CreateScreenTurnedOnAtomMatcher() { return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn", android::view::DisplayStateEnum::DISPLAY_STATE_ON); @@ -335,22 +334,46 @@ State CreateScreenStateWithOnOffMap(int64_t screenOnId, int64_t screenOffId) { return state; } +State CreateScreenStateWithSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId) { + State state; + state.set_id(StringToId("ScreenStateSimpleOnOff")); + state.set_atom_id(util::SCREEN_STATE_CHANGED); + + auto map = CreateScreenStateSimpleOnOffMap(screenOnId, screenOffId); + *state.mutable_map() = map; + + return state; +} + StateMap_StateGroup CreateScreenStateOnGroup(int64_t screenOnId) { StateMap_StateGroup group; group.set_group_id(screenOnId); - group.add_value(2); - group.add_value(5); - group.add_value(6); + group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON); + group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_VR); + group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND); return group; } StateMap_StateGroup CreateScreenStateOffGroup(int64_t screenOffId) { StateMap_StateGroup group; group.set_group_id(screenOffId); - group.add_value(0); - group.add_value(1); - group.add_value(3); - group.add_value(4); + group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE); + group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND); + return group; +} + +StateMap_StateGroup CreateScreenStateSimpleOnGroup(int64_t screenOnId) { + StateMap_StateGroup group; + group.set_group_id(screenOnId); + group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON); + return group; +} + +StateMap_StateGroup CreateScreenStateSimpleOffGroup(int64_t screenOffId) { + StateMap_StateGroup group; + group.set_group_id(screenOffId); + group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_OFF); return group; } @@ -361,6 +384,13 @@ StateMap CreateScreenStateOnOffMap(int64_t screenOnId, int64_t screenOffId) { return map; } +StateMap CreateScreenStateSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId) { + StateMap map; + *map.add_group() = CreateScreenStateSimpleOnGroup(screenOnId); + *map.add_group() = CreateScreenStateSimpleOffGroup(screenOffId); + return map; +} + void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combinationPredicate) { combinationPredicate->mutable_combination()->add_predicate(predicate.id()); diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index dc012c5381eb..6a5d5da2895c 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -149,17 +149,30 @@ State CreateUidProcessState(); // Create State proto for overlay state atom. State CreateOverlayState(); +// Create State proto for screen state atom with on/off map. State CreateScreenStateWithOnOffMap(int64_t screenOnId, int64_t screenOffId); +// Create State proto for screen state atom with simple on/off map. +State CreateScreenStateWithSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId); + // Create StateGroup proto for ScreenState ON group StateMap_StateGroup CreateScreenStateOnGroup(int64_t screenOnId); // Create StateGroup proto for ScreenState OFF group StateMap_StateGroup CreateScreenStateOffGroup(int64_t screenOffId); +// Create StateGroup proto for simple ScreenState ON group +StateMap_StateGroup CreateScreenStateSimpleOnGroup(int64_t screenOnId); + +// Create StateGroup proto for simple ScreenState OFF group +StateMap_StateGroup CreateScreenStateSimpleOffGroup(int64_t screenOffId); + // Create StateMap proto for ScreenState ON/OFF map StateMap CreateScreenStateOnOffMap(int64_t screenOnId, int64_t screenOffId); +// Create StateMap proto for simple ScreenState ON/OFF map +StateMap CreateScreenStateSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId); + // Add a predicate to the predicate combination. void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination); 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/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 16e5156c10de..66b8cabe03c3 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -162,12 +162,9 @@ public class StorageManager { /** {@hide} */ public static final String PROP_SETTINGS_FUSE = FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.SETTINGS_FUSE_FLAG; - /** - * Property that determines whether {@link OP_LEGACY_STORAGE} is sticky for - * legacy apps. - * @hide - */ - public static final String PROP_LEGACY_OP_STICKY = "persist.sys.legacy_storage_sticky"; + /** {@hide} */ + public static final String PROP_FORCED_SCOPED_STORAGE_WHITELIST = + "forced_scoped_storage_whitelist"; /** {@hide} */ public static final String UUID_PRIVATE_INTERNAL = null; diff --git a/core/java/android/util/AtomicFile.java b/core/java/android/util/AtomicFile.java index cf7ed9b0566d..1dd4cbb403db 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,31 @@ 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); + } } + + // Don't delete mNewName here - it was okay to call openRead() between startWrite() and + // finishWrite(), and we have to keep supporting it. 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 +249,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/view/InsetsController.java b/core/java/android/view/InsetsController.java index d12a1221ac95..758062f41428 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -568,15 +568,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private void updateState(InsetsState newState) { mState.setDisplayFrame(newState.getDisplayFrame()); for (int i = newState.getSourcesCount() - 1; i >= 0; i--) { - final InsetsSource source = newState.sourceAt(i); - final int type = source.getType(); - final InsetsSourceConsumer consumer = getSourceConsumer(type); - consumer.updateSource(source); - mHost.updateCompatSysUiVisibility(type, source.isVisible(), - consumer.getControl() != null); + InsetsSource source = newState.sourceAt(i); + getSourceConsumer(source.getType()).updateSource(source); } for (int i = mState.getSourcesCount() - 1; i >= 0; i--) { - final InsetsSource source = mState.sourceAt(i); + InsetsSource source = mState.sourceAt(i); if (newState.peekSource(source.getType()) == null) { mState.removeSource(source.getType()); } @@ -1011,6 +1007,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } /** + * @see ViewRootImpl#updateCompatSysUiVisibility(int, boolean, boolean) + */ + public void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible, + boolean hasControl) { + mHost.updateCompatSysUiVisibility(type, visible, hasControl); + } + + /** * Called when current window gains focus. */ public void onWindowFocusGained() { diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index a0cdcfeefe3f..df3ac8787b57 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -18,6 +18,7 @@ package android.view; import static android.view.InsetsController.ANIMATION_TYPE_NONE; import static android.view.InsetsController.AnimationType; +import static android.view.InsetsState.getDefaultVisibility; import static android.view.InsetsState.toPublicType; import android.annotation.IntDef; @@ -82,7 +83,7 @@ public class InsetsSourceConsumer { mState = state; mTransactionSupplier = transactionSupplier; mController = controller; - mRequestedVisible = InsetsState.getDefaultVisibility(type); + mRequestedVisible = getDefaultVisibility(type); } /** @@ -200,12 +201,20 @@ public class InsetsSourceConsumer { } boolean applyLocalVisibilityOverride() { + final InsetsSource source = mState.peekSource(mType); + final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType); + final boolean hasControl = mSourceControl != null; + + // We still need to let the legacy app know the visibility change even if we don't have the + // control. + mController.updateCompatSysUiVisibility( + mType, hasControl ? mRequestedVisible : isVisible, hasControl); // If we don't have control, we are not able to change the visibility. - if (mSourceControl == null) { + if (!hasControl) { return false; } - if (mState.getSource(mType).isVisible() == mRequestedVisible) { + if (isVisible == mRequestedVisible) { return false; } mState.getSource(mType).setVisible(mRequestedVisible); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index d928356d8600..511e75541ba7 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1977,6 +1977,7 @@ public final class ViewRootImpl implements ViewParent, (mCompatibleVisibilityInfo.globalVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE) | (mAttachInfo.mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE); if (mDispatchedSystemUiVisibility != mCompatibleVisibilityInfo.globalVisibility) { + mHandler.removeMessages(MSG_DISPATCH_SYSTEM_UI_VISIBILITY); mHandler.sendMessage(mHandler.obtainMessage( MSG_DISPATCH_SYSTEM_UI_VISIBILITY, mCompatibleVisibilityInfo)); } @@ -2031,8 +2032,10 @@ public final class ViewRootImpl implements ViewParent, } } else { info.globalVisibility |= systemUiFlag; + info.localChanges &= ~systemUiFlag; } if (mDispatchedSystemUiVisibility != info.globalVisibility) { + mHandler.removeMessages(MSG_DISPATCH_SYSTEM_UI_VISIBILITY); mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, info)); } } diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 39c7210d8dac..301ce9f013e4 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -166,6 +166,8 @@ public abstract class ContentCaptureSession implements AutoCloseable { public static final int FLUSH_REASON_IDLE_TIMEOUT = 5; /** @hide */ public static final int FLUSH_REASON_TEXT_CHANGE_TIMEOUT = 6; + /** @hide */ + public static final int FLUSH_REASON_SESSION_CONNECTED = 7; /** @hide */ @IntDef(prefix = { "FLUSH_REASON_" }, value = { @@ -174,7 +176,8 @@ public abstract class ContentCaptureSession implements AutoCloseable { FLUSH_REASON_SESSION_STARTED, FLUSH_REASON_SESSION_FINISHED, FLUSH_REASON_IDLE_TIMEOUT, - FLUSH_REASON_TEXT_CHANGE_TIMEOUT + FLUSH_REASON_TEXT_CHANGE_TIMEOUT, + FLUSH_REASON_SESSION_CONNECTED }) @Retention(RetentionPolicy.SOURCE) public @interface FlushReason{} @@ -609,6 +612,8 @@ public abstract class ContentCaptureSession implements AutoCloseable { return "IDLE"; case FLUSH_REASON_TEXT_CHANGE_TIMEOUT: return "TEXT_CHANGE"; + case FLUSH_REASON_SESSION_CONNECTED: + return "CONNECTED"; default: return "UNKOWN-" + reason; } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 893d38dcfde7..6eb71f747be6 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -273,6 +273,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } else { mState = resultCode; mDisabled.set(false); + // Flush any pending data immediately as buffering forced until now. + flushIfNeeded(FLUSH_REASON_SESSION_CONNECTED); } if (sVerbose) { Log.v(TAG, "handleSessionStarted() result: id=" + mId + " resultCode=" + resultCode diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index f78ec7c439a1..0807f4162162 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -219,17 +219,34 @@ public class WebChromeClient { } /** - * Tell the client to display a confirm dialog to the user. If the client - * returns {@code true}, WebView will assume that the client will handle the - * confirm dialog and call the appropriate JsResult method. If the - * client returns false, a default value of {@code false} will be returned to - * javascript. The default behavior is to return {@code false}. + * Notify the host application that the web page wants to display a + * JavaScript {@code confirm()} dialog. + * <p>The default behavior if this method returns {@code false} or is not + * overridden is to show a dialog containing the message and suspend + * JavaScript execution until the dialog is dismissed. The default dialog + * will return {@code true} to the JavaScript {@code confirm()} code when + * the user presses the 'confirm' button, and will return {@code false} to + * the JavaScript code when the user presses the 'cancel' button or + * dismisses the dialog. + * <p>To show a custom dialog, the app should return {@code true} from this + * method, in which case the default dialog will not be shown and JavaScript + * execution will be suspended. The app should call + * {@code JsResult.confirm()} or {@code JsResult.cancel()} when the custom + * dialog is dismissed. + * <p>To suppress the dialog and allow JavaScript execution to continue, + * call {@code JsResult.confirm()} or {@code JsResult.cancel()} immediately + * and then return {@code true}. + * <p>Note that if the {@link WebChromeClient} is {@code null}, the default + * dialog will be suppressed and the default value of {@code false} will be + * returned to the JavaScript code immediately. + * * @param view The WebView that initiated the callback. * @param url The url of the page requesting the dialog. * @param message Message to be displayed in the window. * @param result A JsResult used to send the user's response to * javascript. - * @return boolean Whether the client will handle the confirm dialog. + * @return boolean {@code true} if the request is handled or ignored. + * {@code false} if WebView needs to show the default dialog. */ public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { @@ -237,18 +254,33 @@ public class WebChromeClient { } /** - * Tell the client to display a prompt dialog to the user. If the client - * returns {@code true}, WebView will assume that the client will handle the - * prompt dialog and call the appropriate JsPromptResult method. If the - * client returns false, a default value of {@code false} will be returned to to - * javascript. The default behavior is to return {@code false}. + * Notify the host application that the web page wants to display a + * JavaScript {@code prompt()} dialog. + * <p>The default behavior if this method returns {@code false} or is not + * overridden is to show a dialog containing the message and suspend + * JavaScript execution until the dialog is dismissed. Once the dialog is + * dismissed, JavaScript {@code prompt()} will return the string that the + * user typed in, or null if the user presses the 'cancel' button. + * <p>To show a custom dialog, the app should return {@code true} from this + * method, in which case the default dialog will not be shown and JavaScript + * execution will be suspended. The app should call + * {@code JsPromptResult.confirm(result)} when the custom dialog is + * dismissed. + * <p>To suppress the dialog and allow JavaScript execution to continue, + * call {@code JsPromptResult.confirm(result)} immediately and then + * return {@code true}. + * <p>Note that if the {@link WebChromeClient} is {@code null}, the default + * dialog will be suppressed and {@code null} will be returned to the + * JavaScript code immediately. + * * @param view The WebView that initiated the callback. * @param url The url of the page requesting the dialog. * @param message Message to be displayed in the window. * @param defaultValue The default value displayed in the prompt dialog. * @param result A JsPromptResult used to send the user's reponse to * javascript. - * @return boolean Whether the client will handle the prompt dialog. + * @return boolean {@code true} if the request is handled or ignored. + * {@code false} if WebView needs to show the default dialog. */ public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { 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..d4cc636ec60b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5474,15 +5474,17 @@ 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" /> + <action android:name="android.intent.action.LOAD_DATA"/> </intent-filter> </service> <provider android:name="com.android.server.textclassifier.IconsContentProvider" android:authorities="com.android.textclassifier.icons" + android:singleUser="true" android:enabled="true" android:exported="true"> </provider> 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..ac808df7ef5d 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -42,8 +42,10 @@ <item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item> <item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item> <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_media</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 +61,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> @@ -96,6 +97,7 @@ <string translatable="false" name="status_bar_airplane">airplane</string> <string translatable="false" name="status_bar_sensors_off">sensors_off</string> <string translatable="false" name="status_bar_screen_record">screen_record</string> + <string translatable="false" name="status_bar_media">media</string> <!-- Flag indicating whether the surface flinger has limited alpha compositing functionality in hardware. If set, the window @@ -3329,6 +3331,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..758a4f7baffc 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" /> @@ -2913,6 +2916,7 @@ <java-symbol type="string" name="status_bar_camera" /> <java-symbol type="string" name="status_bar_sensors_off" /> <java-symbol type="string" name="status_bar_screen_record" /> + <java-symbol type="string" name="status_bar_media" /> <!-- Locale picker --> <java-symbol type="id" name="locale_search_menu" /> 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/graphics/java/android/graphics/PorterDuff.java b/graphics/java/android/graphics/PorterDuff.java index 1275cb9ca4f9..eb940e2f9017 100644 --- a/graphics/java/android/graphics/PorterDuff.java +++ b/graphics/java/android/graphics/PorterDuff.java @@ -30,8 +30,6 @@ public class PorterDuff { /** * {@usesMathJax} * - * <h3>Porter-Duff</h3> - * * <p>The name of the parent class is an homage to the work of Thomas Porter and * Tom Duff, presented in their seminal 1984 paper titled "Compositing Digital Images". * In this paper, the authors describe 12 compositing operators that govern how to diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index acd90a7f10cf..6df62c0a0cba 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -369,6 +369,13 @@ public final class KeyChain { * {@link Activity#RESULT_OK} will be returned if a credential was * successfully installed, otherwise {@link * Activity#RESULT_CANCELED} will be returned. + * + * <p>Starting from {@link android.os.Build.VERSION_CODES#R}, the intent returned by this + * method cannot be used for installing CA certificates. Since CA certificates can only be + * installed via Settings, the app should provide the user with a file containing the + * CA certificate. One way to do this would be to use the {@link android.provider.MediaStore} + * API to write the certificate to the {@link android.provider.MediaStore.Downloads} + * collection. */ @NonNull public static Intent createInstallIntent() { 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-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 1729027d9613..66787c2a8663 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Gedeaktiveer"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Geaktiveer"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Jou toestel moet herselflaai om hierdie verandering toe te pas. Herselflaai nou of kanselleer."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Werk-<xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index fad7a82aae30..ead0ebfa73bd 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ተሰናክሏል"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ነቅቷል"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"የእርስዎን መሣሪያ ይህ ለው ለማመልከት እንደገና መነሣት አለበት። አሁን እንደገና ያስነሡ ወይም ይተዉት።"</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"የስራ <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index b3cc83282d7f..c1f4fd95813f 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -195,7 +195,7 @@ </string-array> <string name="choose_profile" msgid="343803890897657450">"اختيار ملف شخصي"</string> <string name="category_personal" msgid="6236798763159385225">"شخصي"</string> - <string name="category_work" msgid="4014193632325996115">"العمل"</string> + <string name="category_work" msgid="4014193632325996115">"للعمل"</string> <string name="development_settings_title" msgid="140296922921597393">"خيارات مطور البرامج"</string> <string name="development_settings_enable" msgid="4285094651288242183">"تفعيل خيارات المطورين"</string> <string name="development_settings_summary" msgid="8718917813868735095">"تعيين خيارات تطوير التطبيق"</string> @@ -279,7 +279,7 @@ <string name="private_dns_mode_off" msgid="7065962499349997041">"غير مفعّل"</string> <string name="private_dns_mode_opportunistic" msgid="1947864819060442354">"تلقائي"</string> <string name="private_dns_mode_provider" msgid="3619040641762557028">"اسم مضيف مزوّد \"نظام أسماء النطاقات الخاص\""</string> - <string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"يُرجى إدخال اسم مضيف \"مزوّد نظام أسماء النطاقات\""</string> + <string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"يُرجى إدخال اسم مضيف DNS"</string> <string name="private_dns_mode_provider_failure" msgid="8356259467861515108">"تعذّر الاتصال"</string> <string name="wifi_display_certification_summary" msgid="8111151348106907513">"عرض خيارات شهادة عرض شاشة لاسلكي"</string> <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"زيادة مستوى تسجيل Wi-Fi، وعرض لكل SSID RSSI في منتقي Wi-Fi"</string> @@ -318,10 +318,10 @@ <string name="hdcp_checking_title" msgid="3155692785074095986">"التحقق من HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"تعيين سلوك التحقق من HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"تصحيح الأخطاء"</string> - <string name="debug_app" msgid="8903350241392391766">"تحديد التطبيق لتصحيحه"</string> + <string name="debug_app" msgid="8903350241392391766">"اختيار التطبيق لتصحيحه"</string> <string name="debug_app_not_set" msgid="1934083001283807188">"لم يتم تعيين تطبيق لتصحيحه"</string> <string name="debug_app_set" msgid="6599535090477753651">"تطبيق التصحيح: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> - <string name="select_application" msgid="2543228890535466325">"تحديد تطبيق"</string> + <string name="select_application" msgid="2543228890535466325">"اختيار تطبيق"</string> <string name="no_application" msgid="9038334538870247690">"لا شيء"</string> <string name="wait_for_debugger" msgid="7461199843335409809">"انتظار برنامج التصحيح"</string> <string name="wait_for_debugger_summary" msgid="6846330006113363286">"ينتظر التطبيق قيد التصحيح انضمام برنامج التصحيح قبل التنفيذ"</string> @@ -557,6 +557,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غير مفعّل"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"مفعّل"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"يجب إعادة تشغيل جهازك ليتم تطبيق هذا التغيير. يمكنك إعادة التشغيل الآن أو إلغاء التغيير."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> المخصّص للعمل"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index ad344c3803bc..b5d642a9ae8f 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -549,14 +549,11 @@ <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ কৰক"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string> <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string> - <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) --> - <skip /> - <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) --> - <skip /> - <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) --> - <skip /> - <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) --> - <skip /> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ডিভাইচ ডিফ’ল্ট"</string> + <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"অক্ষম কৰা আছে"</string> + <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"সক্ষম কৰা আছে"</string> + <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"এই সলনিটো কার্যকৰী হ’বলৈ আপোনাৰ ডিভাইচটো ৰিবুট কৰিবই লাগিব। এতিয়াই ৰিবুট কৰক অথবা বাতিল কৰক।"</string> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"কৰ্মস্থান <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index ab7ea8c96d07..aecaf704b02f 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -155,7 +155,7 @@ <string name="launch_defaults_some" msgid="3631650616557252926">"Bəzi susmaya görələr təyin edilib"</string> <string name="launch_defaults_none" msgid="8049374306261262709">"Susmaya görələr təyin edilməyib."</string> <string name="tts_settings" msgid="8130616705989351312">"Mətndən-danışığa parametrləri"</string> - <string name="tts_settings_title" msgid="7602210956640483039">"Mətndən-nitqə çıxışı"</string> + <string name="tts_settings_title" msgid="7602210956640483039">"Mətndən-nitqə daxiletmə"</string> <string name="tts_default_rate_title" msgid="3964187817364304022">"Nitq diapazonu"</string> <string name="tts_default_rate_summary" msgid="3781937042151716987">"Mətnin səsləndirilmə sürəti"</string> <string name="tts_default_pitch_title" msgid="6988592215554485479">"Pitç"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiv"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu dəyişikliyin tətbiq edilməsi üçün cihaz yenidən başladılmalıdır. İndi yenidən başladın və ya ləğv edin."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"İş <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index da47bf27f5c7..092bd42e60ce 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -554,6 +554,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Morate da restartujete uređaj da bi se ova promena primenila. Restartujte ga odmah ili otkažite."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> za posao"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 2761d47c001a..d610973d891c 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -195,7 +195,7 @@ </string-array> <string name="choose_profile" msgid="343803890897657450">"Выбраць профіль"</string> <string name="category_personal" msgid="6236798763159385225">"Асабісты"</string> - <string name="category_work" msgid="4014193632325996115">"Рабочы"</string> + <string name="category_work" msgid="4014193632325996115">"Працоўны"</string> <string name="development_settings_title" msgid="140296922921597393">"Параметры распрацоўшчыка"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Уключыць параметры распрацоўшчыка"</string> <string name="development_settings_summary" msgid="8718917813868735095">"Налада параметраў для распрацоўкі прыкладанняў"</string> @@ -555,6 +555,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Выключана"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Уключана"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Перазагрузіце прыладу, каб прымяніць гэта змяненне. Перазагрузіце ці скасуйце."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (праца)"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 8150710f0db9..5c3fe75857dc 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -419,7 +419,7 @@ <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномалия (червено – зелено)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомалия (синьо – жълто)"</string> <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Корекция на цветове"</string> - <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"Корекцията на цветове ви позволява да коригирате това, как цветовете се показват на устройството ви"</string> + <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"Функцията „Корекция на цветове“ ви позволява да коригирате това, как цветовете се показват на устройството ви"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Заменено от „<xliff:g id="TITLE">%1$s</xliff:g>“"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Още около <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Активирано"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"За да бъде приложена тази промяна, устройството ви трябва да бъде рестартирано. Рестартирайте сега или анулирайте."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> за работа"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 7bae6eef1aaa..27d370714a0d 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -195,7 +195,7 @@ </string-array> <string name="choose_profile" msgid="343803890897657450">"প্রোফাইল বেছে নিন"</string> <string name="category_personal" msgid="6236798763159385225">"ব্যক্তিগত"</string> - <string name="category_work" msgid="4014193632325996115">"কর্মক্ষেত্র"</string> + <string name="category_work" msgid="4014193632325996115">"অফিস"</string> <string name="development_settings_title" msgid="140296922921597393">"ডেভেলপার বিকল্প"</string> <string name="development_settings_enable" msgid="4285094651288242183">"ডেভেলপার বিকল্প সক্ষম করুন"</string> <string name="development_settings_summary" msgid="8718917813868735095">"অ্যাপ্লিকেশান উন্নয়নের জন্য বিকল্পগুলি সেট করুন"</string> @@ -485,7 +485,7 @@ <string name="ims_reg_title" msgid="8197592958123671062">"IMS রেজিস্ট্রেশনের স্থিতি"</string> <string name="ims_reg_status_registered" msgid="884916398194885457">"রেজিস্টার করা"</string> <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"রেজিস্টার করা নয়"</string> - <string name="status_unavailable" msgid="5279036186589861608">"অনুপলব্ধ"</string> + <string name="status_unavailable" msgid="5279036186589861608">"অনুপলভ্য"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC র্যান্ডমাইজ করা হয়েছে"</string> <plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139"> <item quantity="one">%1$dটি ডিভাইস কানেক্ট</item> @@ -549,14 +549,11 @@ <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string> <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string> - <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) --> - <skip /> - <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) --> - <skip /> - <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) --> - <skip /> - <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) --> - <skip /> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ডিভাইসের ডিফল্ট"</string> + <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"বন্ধ করা আছে"</string> + <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"চালু করা আছে"</string> + <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"এই পরিবর্তনটি প্রয়োগ করার জন্য আপনার ডিভাইসটি অবশ্যই রিবুট করতে হবে। এখন রিবুট করুন বা বাতিল করুন।"</string> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"অফিস <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml index 6489cefdc551..6595c226a171 100644 --- a/packages/SettingsLib/res/values-bs/arrays.xml +++ b/packages/SettingsLib/res/values-bs/arrays.xml @@ -40,7 +40,7 @@ <item msgid="8339720953594087771">"Povezivanje na mrežu <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item> <item msgid="3028983857109369308">"Autentifikacija s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item> <item msgid="4287401332778341890">"Dobivanje IP adrese iz mreže <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item> - <item msgid="1043944043827424501">"Povezano na mrežu <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item> + <item msgid="1043944043827424501">"Povezano s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item> <item msgid="7445993821842009653">"Suspendirano"</item> <item msgid="1175040558087735707">"Prekidanje veze s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item> <item msgid="699832486578171722">"Isključen"</item> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index fe7f8c165864..a9d9e7025236 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -452,7 +452,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sporo punjenje"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Priključen, trenutno se ne može puniti"</string> + <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Priključeno, trenutno se ne može puniti"</string> <string name="battery_info_status_full" msgid="4443168946046847468">"Puna"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Pod kontrolom administratora"</string> <string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string> @@ -554,5 +554,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Morate ponovo pokrenuti uređaj da se ova promjena primijeni. Ponovo pokrenite odmah ili otkažite."</string> - <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> za posao"</string> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Poslovna aplikacija <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 7dad950c5d47..5ed0f1b2d3ca 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -222,7 +222,7 @@ <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"Empremta digital del dispositiu: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string> <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"No s\'ha pogut connectar"</string> <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Assegura\'t que <xliff:g id="DEVICE_NAME">%1$s</xliff:g> estigui connectat a la xarxa correcta"</string> - <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Vincular amb el dispositiu"</string> + <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Vincula amb el dispositiu"</string> <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Codi de vinculació Wi‑Fi"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"No s\'ha pogut vincular"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Assegura\'t que el dispositiu estigui connectat a la mateixa xarxa."</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivat"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Has de reiniciar el teu dispositiu perquè s\'apliquin els canvis. Reinicia\'l ara o cancel·la."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> de la feina"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 51548f55e430..d4d37353fb82 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -555,6 +555,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuto"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuto"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Aby se tato změna projevila, je třeba zařízení restartovat. Restartujte zařízení nebo zrušte akci."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Pracovní <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 88f8e92d8093..89dbf2581746 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -433,7 +433,7 @@ <string name="power_discharge_by" msgid="4113180890060388350">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_discharge_by_only" msgid="92545648425937000">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Indtil <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Enheden løber muligvis tør for batteri inden <xliff:g id="TIME">%1$s</xliff:g>"</string> + <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Batteriet aflades muligvis inden <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Der er mindre end <xliff:g id="THRESHOLD">%1$s</xliff:g> tilbage"</string> <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Der er mindre end <xliff:g id="THRESHOLD">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Der er mere end <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiveret"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiveret"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Din enhed skal genstartes for at denne enhed bliver anvendt. Genstart nu, eller annuller."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> – arbejde"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index edd15806c2ed..c2030b162e56 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -231,7 +231,7 @@ <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Das Gerät konnte nicht gekoppelt werden. Der QR-Code war nicht korrekt oder das Gerät ist nicht mit demselben Netzwerk verbunden."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP-Adresse & Port"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR-Code scannen"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Scanne einen QR-Code, um ein Gerät über WLAN zu koppeln"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Scanne einen QR-Code, um das Gerät über WLAN zu koppeln"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Bitte stell eine WLAN-Verbindung her"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"ADB, Debug, Dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"Verknüpfung zu Fehlerbericht"</string> @@ -507,7 +507,7 @@ <string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Dauer"</string> <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Jedes Mal fragen"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Bis zur Deaktivierung"</string> - <string name="time_unit_just_now" msgid="3006134267292728099">"gerade eben"</string> + <string name="time_unit_just_now" msgid="3006134267292728099">"Gerade eben"</string> <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Smartphone-Lautsprecher"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Verbindung kann nicht hergestellt werden. Schalte das Gerät aus & und wieder ein."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Netzbetriebenes Audiogerät"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiviert"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiviert"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Damit diese Änderung übernommen wird, musst du dein Gerät neu starten. Du kannst es jetzt neu starten oder den Vorgang abbrechen."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (geschäftlich)"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index c5c0e074f6f8..bfc183e40a53 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -194,8 +194,8 @@ <item msgid="581904787661470707">"Ταχύτατη"</item> </string-array> <string name="choose_profile" msgid="343803890897657450">"Επιλογή προφίλ"</string> - <string name="category_personal" msgid="6236798763159385225">"Προσωπικό"</string> - <string name="category_work" msgid="4014193632325996115">"Εργασία"</string> + <string name="category_personal" msgid="6236798763159385225">"Προσωπικός"</string> + <string name="category_work" msgid="4014193632325996115">"Εργασίας"</string> <string name="development_settings_title" msgid="140296922921597393">"Επιλογές για προγραμματιστές"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Ενεργοποίηση επιλογών για προγραμματιστές"</string> <string name="development_settings_summary" msgid="8718917813868735095">"Ορισμός επιλογών για ανάπτυξη εφαρμογής"</string> @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ενεργή"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Για να εφαρμοστεί αυτή η αλλαγή, θα πρέπει να επανεκκινήσετε τη συσκευή σας. Επανεκκίνηση τώρα ή ακύρωση."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Εργασία <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index a1c8b041dab2..9d869ba30717 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -229,7 +229,7 @@ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Pair device over Wi‑Fi by scanning a QR code"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Pairing device…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Failed to pair the device. Either the QR code was incorrect, or the device is not connected to the same network."</string> - <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP address & port"</string> + <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP address and port"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Scan QR code"</string> <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Pair device over Wi‑Fi by scanning a QR code"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Please connect to a Wi‑Fi network"</string> @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Work <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index a1c8b041dab2..9d869ba30717 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -229,7 +229,7 @@ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Pair device over Wi‑Fi by scanning a QR code"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Pairing device…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Failed to pair the device. Either the QR code was incorrect, or the device is not connected to the same network."</string> - <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP address & port"</string> + <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP address and port"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Scan QR code"</string> <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Pair device over Wi‑Fi by scanning a QR code"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Please connect to a Wi‑Fi network"</string> @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Work <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index a1c8b041dab2..9d869ba30717 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -229,7 +229,7 @@ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Pair device over Wi‑Fi by scanning a QR code"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Pairing device…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Failed to pair the device. Either the QR code was incorrect, or the device is not connected to the same network."</string> - <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP address & port"</string> + <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP address and port"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Scan QR code"</string> <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Pair device over Wi‑Fi by scanning a QR code"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Please connect to a Wi‑Fi network"</string> @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Work <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index a1c8b041dab2..9d869ba30717 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -229,7 +229,7 @@ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Pair device over Wi‑Fi by scanning a QR code"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Pairing device…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Failed to pair the device. Either the QR code was incorrect, or the device is not connected to the same network."</string> - <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP address & port"</string> + <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP address and port"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Scan QR code"</string> <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Pair device over Wi‑Fi by scanning a QR code"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Please connect to a Wi‑Fi network"</string> @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Work <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 38ac8f1fc554..77658243864e 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Work <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index c6d0d56d409b..6a76bebc78ee 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -61,7 +61,7 @@ <string name="speed_label_medium" msgid="9078405312828606976">"Media"</string> <string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string> <string name="speed_label_very_fast" msgid="8215718029533182439">"Muy rápida"</string> - <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Vencido"</string> + <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Vencida"</string> <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconectado"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Desconectando…"</string> @@ -153,7 +153,7 @@ <string name="unknown" msgid="3544487229740637809">"Desconocido"</string> <string name="running_process_item_user_label" msgid="3988506293099805796">"Usuario: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> <string name="launch_defaults_some" msgid="3631650616557252926">"Configuraciones predeterminadas establecidas"</string> - <string name="launch_defaults_none" msgid="8049374306261262709">"No hay configuraciones predeterminadas establecidas."</string> + <string name="launch_defaults_none" msgid="8049374306261262709">"No se establecieron configuraciones predeterminadas"</string> <string name="tts_settings" msgid="8130616705989351312">"Configuración de texto a voz"</string> <string name="tts_settings_title" msgid="7602210956640483039">"Salida de texto a voz"</string> <string name="tts_default_rate_title" msgid="3964187817364304022">"Velocidad de voz"</string> @@ -213,7 +213,7 @@ <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Para ver y usar los dispositivos disponibles, activa la depuración inalámbrica"</string> <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Vincular dispositivo mediante código QR"</string> <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Vincular dispositivos nuevos mediante escáner de código QR"</string> - <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Vincular dispositivo mediante código de sincroniz."</string> + <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Vincular dispositivo con código de sincronización"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Vincular dispositivos nuevos mediante código de seis dígitos"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"Dispositivos vinculados"</string> <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Conectado actualmente"</string> @@ -235,7 +235,7 @@ <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Conéctate a una red Wi-Fi"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, debug, dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"Acceso directo para informes de errores"</string> - <string name="bugreport_in_power_summary" msgid="1885529649381831775">"Mostrar un botón en el menú de encendido para realizar un informe de errores"</string> + <string name="bugreport_in_power_summary" msgid="1885529649381831775">"Muestra un botón en el menú de encendido para realizar un informe de errores"</string> <string name="keep_screen_on" msgid="1187161672348797558">"Permanecer activo"</string> <string name="keep_screen_on_summary" msgid="1510731514101925829">"La pantalla nunca quedará inactiva mientras el dispositivo se esté cargando."</string> <string name="bt_hci_snoop_log" msgid="7291287955649081448">"Registro de Bluetooth HCI"</string> @@ -331,7 +331,7 @@ <string name="media_category" msgid="8122076702526144053">"Multimedia"</string> <string name="debug_monitoring_category" msgid="1597387133765424994">"Supervisión"</string> <string name="strict_mode" msgid="889864762140862437">"Modo estricto"</string> - <string name="strict_mode_summary" msgid="1838248687233554654">"Destello por op. de apps en la conversación principal"</string> + <string name="strict_mode_summary" msgid="1838248687233554654">"Destello por op. de apps en el subproceso principal"</string> <string name="pointer_location" msgid="7516929526199520173">"Ubicación del puntero"</string> <string name="pointer_location_summary" msgid="957120116989798464">"Superponer capa en pant. para mostrar puntos tocados"</string> <string name="show_touches" msgid="8437666942161289025">"Mostrar presiones"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Debes reiniciar el dispositivo para que se aplique el cambio. Reinícialo ahora o cancela la acción."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> de trabajo"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 55b5cabc8e31..010b85b86df3 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -61,7 +61,7 @@ <string name="speed_label_medium" msgid="9078405312828606976">"Media"</string> <string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string> <string name="speed_label_very_fast" msgid="8215718029533182439">"Muy rápida"</string> - <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Caducado"</string> + <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Caducada"</string> <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconectado"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Desconectando…"</string> @@ -506,7 +506,7 @@ <string name="alarm_template_far" msgid="6382760514842998629">"Fecha: <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duración"</string> <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Preguntar siempre"</string> - <string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que se desactive"</string> + <string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"justo ahora"</string> <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altavoz del teléfono"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"No se ha podido conectar; reinicia el dispositivo"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Es necesario reiniciar tu dispositivo para que se apliquen los cambios. Reiniciar ahora o cancelar."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> de trabajo"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 5a115909d076..ad2a56152452 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -419,7 +419,7 @@ <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaalia (punane-roheline)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaalia (sinine-kollane)"</string> <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Värvide korrigeerimine"</string> - <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"Värvikorrigeerimine võimaldab kohandada seadmes kuvatavaid värve"</string> + <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"Värvide korrigeerimine võimaldab kohandada seadmes kuvatavaid värve"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Alistas <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ligikaudu <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäänud"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Keelatud"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Lubatud"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Selle muudatuse rakendamiseks tuleb seade taaskäivitada. Taaskäivitage kohe või tühistage."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Töö: <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 1aed7e84ded3..1ceb738914f2 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Gaituta"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Aldaketa aplikatzeko, berrabiarazi egin behar da gailua. Berrabiaraz ezazu orain, edo utzi bertan behera."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Laneko <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml index 3b05075c1d75..94caa747c829 100644 --- a/packages/SettingsLib/res/values-fa/arrays.xml +++ b/packages/SettingsLib/res/values-fa/arrays.xml @@ -40,7 +40,7 @@ <item msgid="8339720953594087771">"در حال اتصال به <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item> <item msgid="3028983857109369308">"در حال راستیآزمایی با <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item> <item msgid="4287401332778341890">"درحال دریافت نشانی IP از <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item> - <item msgid="1043944043827424501">"متصل شد به <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item> + <item msgid="1043944043827424501">"به <xliff:g id="NETWORK_NAME">%1$s</xliff:g> متصل شد"</item> <item msgid="7445993821842009653">"معلق"</item> <item msgid="1175040558087735707">"اتصال قطع شد از <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item> <item msgid="699832486578171722">"اتصال قطع شد"</item> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index a46d8cddb53d..d3f1c56cd1d7 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -94,12 +94,12 @@ <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"صدای HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"صدای HD"</string> <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"سمعکها"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"متصل به سمعکها"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"به سمعک متصل شد"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"به رسانه صوتی متصل شد"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"به تلفن صوتی متصل شد"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"به سرور انتقال فایل متصل شد"</string> <string name="bluetooth_map_profile_summary_connected" msgid="4141725591784669181">"به نقشه متصل شد"</string> - <string name="bluetooth_sap_profile_summary_connected" msgid="1280297388033001037">"متصل به SAP"</string> + <string name="bluetooth_sap_profile_summary_connected" msgid="1280297388033001037">"به SAP متصل شد"</string> <string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"به سرور انتقال فایل متصل نیست"</string> <string name="bluetooth_hid_profile_summary_connected" msgid="3923653977051684833">"به دستگاه ورودی متصل شد"</string> <string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"برای دسترسی به اینترنت، به دستگاه متصل شد"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غیرفعال"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"برای اعمال این تغییر، دستگاهتان باید راهاندازی مجدد شود. اکنون راهاندازی مجدد کنید یا لغو کنید."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> محل کار"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml index af278cb35fd4..edfd951c0fa6 100644 --- a/packages/SettingsLib/res/values-fi/arrays.xml +++ b/packages/SettingsLib/res/values-fi/arrays.xml @@ -156,7 +156,7 @@ <item msgid="5001852592115448348">", aktiivinen (puhelin)"</item> </string-array> <string-array name="select_logd_size_titles"> - <item msgid="1191094707770726722">"Ei käytössä"</item> + <item msgid="1191094707770726722">"Ei päällä"</item> <item msgid="7839165897132179888">"64 kt"</item> <item msgid="2715700596495505626">"256 kt"</item> <item msgid="7099386891713159947">"1 Mt"</item> @@ -164,13 +164,13 @@ <item msgid="8243549501764402572">"16 Mt"</item> </string-array> <string-array name="select_logd_size_lowram_titles"> - <item msgid="1145807928339101085">"Ei käytössä"</item> + <item msgid="1145807928339101085">"Ei päällä"</item> <item msgid="4064786181089783077">"64 kt"</item> <item msgid="3052710745383602630">"256 kt"</item> <item msgid="3691785423374588514">"1 Mt"</item> </string-array> <string-array name="select_logd_size_summaries"> - <item msgid="409235464399258501">"Ei käytössä"</item> + <item msgid="409235464399258501">"Ei päällä"</item> <item msgid="4195153527464162486">"64 kt / lokipuskuri"</item> <item msgid="7464037639415220106">"256 kt / lokipuskuri"</item> <item msgid="8539423820514360724">"1 Mt / lokipuskuri"</item> @@ -178,13 +178,13 @@ <item msgid="7892098981256010498">"16 Mt / lokipuskuri"</item> </string-array> <string-array name="select_logpersist_titles"> - <item msgid="704720725704372366">"Ei käytössä"</item> + <item msgid="704720725704372366">"Ei päällä"</item> <item msgid="6014837961827347618">"Kaikki"</item> <item msgid="7387060437894578132">"Kaikki paitsi radio"</item> <item msgid="7300881231043255746">"vain kernel"</item> </string-array> <string-array name="select_logpersist_summaries"> - <item msgid="97587758561106269">"Ei käytössä"</item> + <item msgid="97587758561106269">"Ei päällä"</item> <item msgid="7126170197336963369">"Kaikki lokipuskurit"</item> <item msgid="7167543126036181392">"Kaikki paitsi radiolokipuskurit"</item> <item msgid="5135340178556563979">"vain kernel-lokipuskuri"</item> @@ -247,7 +247,7 @@ <item msgid="5023908510820531131">"Kohteessa <xliff:g id="AS_TYPED_COMMAND">adb shell dumpsys gfxinfo</xliff:g>"</item> </string-array> <string-array name="debug_hw_overdraw_entries"> - <item msgid="1968128556747588800">"Ei käytössä"</item> + <item msgid="1968128556747588800">"Ei päällä"</item> <item msgid="3033215374382962216">"Näytä päällekkäiset alueet"</item> <item msgid="3474333938380896988">"Näytä alueet puna-vihersokeille näkyvinä"</item> </string-array> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 8e82cb2b6f47..5ab01c4b51b4 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -153,7 +153,7 @@ <string name="unknown" msgid="3544487229740637809">"Tuntematon"</string> <string name="running_process_item_user_label" msgid="3988506293099805796">"Käyttäjä: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> <string name="launch_defaults_some" msgid="3631650616557252926">"Joitakin oletuksia on asetettu"</string> - <string name="launch_defaults_none" msgid="8049374306261262709">"Oletuksia ei asetettu"</string> + <string name="launch_defaults_none" msgid="8049374306261262709">"Ei oletuksia valittuina"</string> <string name="tts_settings" msgid="8130616705989351312">"Tekstistä puheeksi -asetukset"</string> <string name="tts_settings_title" msgid="7602210956640483039">"Tekstistä puheeksi -toisto"</string> <string name="tts_default_rate_title" msgid="3964187817364304022">"Puheen nopeus"</string> @@ -397,7 +397,7 @@ <item msgid="1282170165150762976">"Digitaaliselle sisällölle parhaiten sopivat värit"</item> </string-array> <string name="inactive_apps_title" msgid="5372523625297212320">"Valmiustilasovellukset"</string> - <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Ei käytössä. Ota käyttöön koskettamalla."</string> + <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Ei päällä. Ota käyttöön koskettamalla."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiivinen. Vaihda koskettamalla."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Sovelluksen valmiusluokka: <xliff:g id="BUCKET"> %s</xliff:g>"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Käynnissä olevat palvelut"</string> @@ -533,8 +533,8 @@ <string name="user_add_user_title" msgid="5457079143694924885">"Lisätäänkö uusi käyttäjä?"</string> <string name="user_add_user_message_long" msgid="1527434966294733380">"Voit jakaa tämän laitteen muiden kanssa luomalla lisää käyttäjiä. Kullakin käyttäjällä on oma tilansa, jota he voivat muokata esimerkiksi omilla sovelluksilla ja taustakuvilla. Käyttäjät voivat myös muokata laiteasetuksia, kuten Wi‑Fi-asetuksia, jotka vaikuttavat laitteen kaikkiin käyttäjiin.\n\nKun lisäät uuden käyttäjän, hänen tulee määrittää oman tilansa asetukset.\n\nKaikki käyttäjät voivat päivittää muiden käyttäjien sovelluksia. Esteettömyysominaisuuksia tai ‑palveluita ei välttämättä siirretä uudelle käyttäjälle."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Kun lisäät uuden käyttäjän, hänen tulee määrittää oman tilansa asetukset.\n\nKaikki käyttäjät voivat päivittää sovelluksia muille käyttäjille."</string> - <string name="user_setup_dialog_title" msgid="8037342066381939995">"Määritetäänkö käyttäjän asetukset nyt?"</string> - <string name="user_setup_dialog_message" msgid="269931619868102841">"Varmista, että käyttäjä voi vastaanottaa laitteen ja määrittää oman tilansa."</string> + <string name="user_setup_dialog_title" msgid="8037342066381939995">"Lisätäänkö käyttäjä nyt?"</string> + <string name="user_setup_dialog_message" msgid="269931619868102841">"Varmista, että käyttäjä voi ottaa laitteen nyt ja määrittää oman tilansa."</string> <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Määritetäänkö profiilin asetukset nyt?"</string> <string name="user_setup_button_setup_now" msgid="1708269547187760639">"Määritä nyt"</string> <string name="user_setup_button_setup_later" msgid="8712980133555493516">"Ei nyt"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ei käytössä"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Käytössä"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Laitteesi on käynnistettävä uudelleen, jotta muutos tulee voimaan. Käynnistä uudelleen nyt tai peruuta."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (työ)"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index a1444444eedd..26e50421cad8 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -195,7 +195,7 @@ </string-array> <string name="choose_profile" msgid="343803890897657450">"Sélectionnez un profil"</string> <string name="category_personal" msgid="6236798763159385225">"Personnel"</string> - <string name="category_work" msgid="4014193632325996115">"Travail"</string> + <string name="category_work" msgid="4014193632325996115">"Professionnel"</string> <string name="development_settings_title" msgid="140296922921597393">"Options pour les concepteurs"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Activer les options pour les concepteurs"</string> <string name="development_settings_summary" msgid="8718917813868735095">"Définir les options pour le développement de l\'application"</string> @@ -231,7 +231,7 @@ <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Échec de l\'association de l\'appareil Soit le code QR est incorrect, soit l\'appareil n\'est pas connecté au même réseau."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Adresse IP et port"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Numériser le code QR"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Associer un appareil par Wi-Fi en numérisant un code QR"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Associer l\'appareil par Wi-Fi en numérisant un code QR"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Veuillez vous connecter à un réseau Wi-Fi"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, débogage, concepteur"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"Raccourci de rapport de bogue"</string> @@ -496,7 +496,7 @@ <string name="cancel" msgid="5665114069455378395">"Annuler"</string> <string name="okay" msgid="949938843324579502">"OK"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activer"</string> - <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activer la fonction « Ne pas déranger »"</string> + <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activer le mode Ne pas déranger"</string> <string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Jamais"</string> <string name="zen_interruption_level_priority" msgid="5392140786447823299">"Priorités seulement"</string> <string name="zen_mode_and_condition" msgid="8877086090066332516">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string> @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Votre appareil doit être redémarré pour que ce changement prenne effet. Redémarrez-le maintenant ou annulez la modification."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (travail)"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index f7bfad8c2567..b8fc50dc028b 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -231,7 +231,7 @@ <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Échec de l\'association à l\'appareil. Le code QR est incorrect, ou l\'appareil n\'est pas connecté au même réseau."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Adresse IP et port"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Scanner un code QR"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Associer l\'appareil via le Wi‑Fi à l\'aide d\'un code QR"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Associez l\'appareil via le Wi‑Fi à l\'aide d\'un code QR"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Connectez-vous à un réseau Wi-Fi"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, débogage, dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"Raccourci vers rapport de bug"</string> @@ -433,7 +433,7 @@ <string name="power_discharge_by" msgid="4113180890060388350">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_discharge_by_only" msgid="92545648425937000">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Jusqu\'à <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"La batterie risque d\'être épuisée à <xliff:g id="TIME">%1$s</xliff:g>"</string> + <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"La batterie risque d\'être épuisée d\'ici <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Vous devez redémarrer l\'appareil pour que cette modification soit appliquée. Redémarrez maintenant ou annulez l\'opération."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (travail)"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 08d2eae5c4ae..8dc58ef6f32f 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -207,7 +207,7 @@ <string name="enable_adb_summary" msgid="3711526030096574316">"Modo de depuración de erros cando o USB está conectado"</string> <string name="clear_adb_keys" msgid="3010148733140369917">"Revogar as autorizacións de depuración por USB"</string> <string name="enable_adb_wireless" msgid="6973226350963971018">"Depuración sen fíos"</string> - <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Modo de depuración de erros ao conectarse a wifi"</string> + <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Modo de depuración de erros ao conectarse á wifi"</string> <string name="adb_wireless_error" msgid="721958772149779856">"Produciuse un erro"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"Depuración sen fíos"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Para ver e usar os dispositivos dispoñibles, activa a depuración sen fíos"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivado"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necesario reiniciar o teu dispositivo para aplicar este cambio. Reiníciao agora ou cancela o cambio."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Aplicación <xliff:g id="APP_NAME">%s</xliff:g> do traballo"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index b88126bf99be..9f69a2c0fa20 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -468,7 +468,7 @@ <string name="charge_length_format" msgid="6941645744588690932">"<xliff:g id="ID_1">%1$s</xliff:g> પહેલાં"</string> <string name="remaining_length_format" msgid="4310625772926171089">"<xliff:g id="ID_1">%1$s</xliff:g> બાકી"</string> <string name="screen_zoom_summary_small" msgid="6050633151263074260">"નાનું"</string> - <string name="screen_zoom_summary_default" msgid="1888865694033865408">"ડિફોલ્ટ"</string> + <string name="screen_zoom_summary_default" msgid="1888865694033865408">"ડિફૉલ્ટ"</string> <string name="screen_zoom_summary_large" msgid="4706951482598978984">"મોટું"</string> <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"વધુ મોટું"</string> <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"સૌથી મોટું"</string> @@ -534,7 +534,7 @@ <string name="user_add_user_message_long" msgid="1527434966294733380">"તમે વધારાના વપરાશકર્તાઓ બનાવીને અન્ય લોકો સાથે આ ડિવાઇસને શેર કરી શકો છો. દરેક વપરાશકર્તા પાસે તેમની પોતાની સ્પેસ છે, જેને તેઓ ઍપ, વૉલપેપર, વગેરે સાથે કસ્ટમાઇઝ કરી શકે છે. વપરાશકર્તાઓ પ્રત્યેક વ્યક્તિને અસર કરતી હોય તેવી ડિવાઇસ સેટિંગ જેમ કે વાઇ-ફાઇને પણ સમાયોજિત કરી શકે છે.\n\nજ્યારે તમે કોઈ નવા વપરાશકર્તાને ઉમેરો છો, ત્યારે તે વ્યક્તિને તેમની સ્પેસ સેટ કરવાની જરૂર પડે છે.\n\nકોઈપણ વપરાશકર્તા અન્ય બધા વપરાશકર્તાઓ માટે ઍપને અપડેટ કરી શકે છે. નવા વપરાશકર્તાને ઍક્સેસિબિલિટી સેટિંગ અને સેવાઓ ટ્રાન્સફર ન પણ થાય."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"જ્યારે તમે કોઈ નવા વપરાશકર્તાને ઉમેરો છો, ત્યારે તે વ્યક્તિને તેમનું સ્થાન સેટ અપ કરવાની જરૂર પડે છે.\n\nકોઈપણ વપરાશકર્તા બધા અન્ય વપરાશકર્તાઓ માટે એપ્લિકેશન્સને અપડેટ કરી શકે છે."</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"અત્યારે જ વપરાશકર્તાને સેટ અપ કરીએ?"</string> - <string name="user_setup_dialog_message" msgid="269931619868102841">"ખાતરી કરો કે વ્યક્તિ ઉપકરણ લેવા અને તેમના સ્થાનનું સેટ અપ કરવા માટે ઉપલબ્ધ છે"</string> + <string name="user_setup_dialog_message" msgid="269931619868102841">"ખાતરી કરો કે વ્યક્તિ ડિવાઇસ લેવા અને તેમના સ્થાનનું સેટ અપ કરવા માટે ઉપલબ્ધ છે"</string> <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"હવે પ્રોફાઇલ સેટ કરીએ?"</string> <string name="user_setup_button_setup_now" msgid="1708269547187760639">"હવે સેટ કરો"</string> <string name="user_setup_button_setup_later" msgid="8712980133555493516">"હમણાં નહીં"</string> @@ -549,14 +549,11 @@ <string name="guest_new_guest" msgid="3482026122932643557">"અતિથિ ઉમેરો"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"અતિથિને કાઢી નાખો"</string> <string name="guest_nickname" msgid="6332276931583337261">"અતિથિ"</string> - <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) --> - <skip /> - <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) --> - <skip /> - <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) --> - <skip /> - <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) --> - <skip /> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ડિવાઇસ ડિફૉલ્ટ"</string> + <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"બંધ છે"</string> + <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ચાલુ છે"</string> + <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"આ ફેરફારને લાગુ કરવા માટે તમારા ડિવાઇસને રીબૂટ કરવાની જરૂર છે. હમણાં જ રીબૂટ કરો કે રદ કરો."</string> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ઑફિસ <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 7dda73de114f..dc8e28bb9df2 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -284,7 +284,7 @@ <string name="wifi_display_certification_summary" msgid="8111151348106907513">"वायरलेस डिसप्ले सर्टिफ़िकेशन के विकल्प दिखाएं"</string> <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"वाई-फ़ाई लॉगिंग का स्तर बढ़ाएं, वाई-फ़ाई पिकर में प्रति SSID RSSI दिखाएं"</string> <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"बैटरी की खपत कम और नेटवर्क की परफ़ॉर्मेंस बेहतर होती है"</string> - <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"जब यह मोड चालू होता है, तब नेटवर्क से कनेक्ट होने पर हर बार इस डिवाइस का MAC पता बदल सकता है. ऐसा तब होता है, जब डिवाइस किसी ऐसे नेटवर्क से जुड़ता है जिस पर MAC पते को बिना किसी तय नियम के बदलने की सुविधा चालू होती है."</string> + <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"जब यह मोड चालू होता है, तब नेटवर्क से कनेक्ट होने पर हर बार इस डिवाइस का मैक पता बदल सकता है. ऐसा तब होता है, जब डिवाइस किसी ऐसे नेटवर्क से जुड़ता है जिस पर मैक पते को बिना किसी तय नियम के बदलने की सुविधा चालू होती है."</string> <string name="wifi_metered_label" msgid="8737187690304098638">"डेटा इस्तेमाल करने की सीमा तय की गई है"</string> <string name="wifi_unmetered_label" msgid="6174142840934095093">"डेटा इस्तेमाल करने की सीमा तय नहीं की गई है"</string> <string name="select_logd_size_title" msgid="1604578195914595173">"लॉगर बफ़र आकार"</string> @@ -418,7 +418,7 @@ <string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"लाल-हरे रंग की पहचान न कर पाना (लाल-हरा)"</string> <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"लाल रंग पहचान न पाना (लाल-हरा)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"नीला रंग पहचान न पाना (नीला-पीला)"</string> - <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"रंग सुधार"</string> + <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"रंग में सुधार करने की सुविधा"</string> <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"रंग में सुधार करने की सुविधा, आपके डिवाइस पर दिखने वाले रंगों में बदलाव करने में मदद करती है"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> के द्वारा ओवरराइड किया गया"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"बंद है"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"चालू है"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"बदली गई सेटिंग को लागू करने के लिए, अपने डिवाइस को फिर से चालू करें. डिवाइस को फिर से चालू करें या रद्द करें."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ऑफ़िस वाला <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 1db40a8e8da5..f8878396d867 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -251,7 +251,7 @@ <string name="wifi_display_certification" msgid="1805579519992520381">"Certifikacija bežičnog prikaza"</string> <string name="wifi_verbose_logging" msgid="1785910450009679371">"Omogući opširnu prijavu na Wi-Fi"</string> <string name="wifi_scan_throttling" msgid="2985624788509913617">"Usporavanje traženja Wi-Fija"</string> - <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Nasum. odabir MAC-a poboljšan Wi‑Fi‑jem"</string> + <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Nasum. odabir MAC-a poboljšan Wi‑Fijem"</string> <string name="mobile_data_always_on" msgid="8275958101875563572">"Mobilni podaci uvijek aktivni"</string> <string name="tethering_hardware_offload" msgid="4116053719006939161">"Hardversko ubrzanje za modemsko povezivanje"</string> <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string> @@ -534,8 +534,8 @@ <string name="user_add_user_title" msgid="5457079143694924885">"Dodati novog korisnika?"</string> <string name="user_add_user_message_long" msgid="1527434966294733380">"Da biste ovaj uređaj dijelili s drugima, možete napraviti dodatne korisnike. Svaki korisnik ima svoj prostor koji može prilagoditi pomoću vlastitih aplikacija, pozadine i tako dalje. Korisnici mogu prilagoditi i postavke uređaja koje utječu na sve ostale korisnike, na primjer Wi‑Fi.\n\nKada dodate novog korisnika, ta osoba mora postaviti svoj prostor.\n\nBilo koji korisnik može ažurirati aplikacije za sve ostale korisnike. Postavke i usluge pristupačnosti možda se neće prenijeti na novog korisnika."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Kada dodate novog korisnika, ta osoba mora postaviti vlastiti prostor.\n\nBilo koji korisnik može ažurirati aplikacije za sve ostale korisnike."</string> - <string name="user_setup_dialog_title" msgid="8037342066381939995">"Postaviti korisnika sada?"</string> - <string name="user_setup_dialog_message" msgid="269931619868102841">"Provjerite može li osoba uzeti uređaj i postaviti svoj prostor"</string> + <string name="user_setup_dialog_title" msgid="8037342066381939995">"Želite li postaviti korisnika?"</string> + <string name="user_setup_dialog_message" msgid="269931619868102841">"Uređaj morate dati toj osobi da sama postavi svoj prostor"</string> <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Želite li sada postaviti profil?"</string> <string name="user_setup_button_setup_now" msgid="1708269547187760639">"Postavi sada"</string> <string name="user_setup_button_setup_later" msgid="8712980133555493516">"Ne sad"</string> @@ -555,4 +555,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Uređaj se mora ponovno pokrenuti da bi se ta promjena primijenila. Ponovo pokrenite uređaj odmah ili odustanite."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> za posao"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 4b2132be42d9..d7d269406f84 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -433,7 +433,7 @@ <string name="power_discharge_by" msgid="4113180890060388350">"Nagyjából eddig bírja: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_discharge_by_only" msgid="92545648425937000">"Nagyjából eddig bírja: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Eddig: <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Az akkumulátor lemerülhet a következő időpontig: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Az akkumulátor lemerülhet: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Kevesebb mint <xliff:g id="THRESHOLD">%1$s</xliff:g> van hátra"</string> <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Kevesebb mint <xliff:g id="THRESHOLD">%1$s</xliff:g> van hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Több mint <xliff:g id="TIME_REMAINING">%1$s</xliff:g> van hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Letiltva"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Engedélyezve"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Az eszközt újra kell indítani, hogy a módosítás megtörténjen. Indítsa újra most, vagy vesse el a módosítást."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Munkahelyi <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 11da054325f1..a5ef6b557e4b 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -433,7 +433,7 @@ <string name="power_discharge_by" msgid="4113180890060388350">"Լիցքը (<xliff:g id="LEVEL">%2$s</xliff:g>) պետք է որ բավականացնի մինչև <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_discharge_by_only" msgid="92545648425937000">"Լիցքը պետք է որ բավականացնի մինչև <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Մինչև <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Մարտկոցի լիցքը կարող է սպառվել մինչև <xliff:g id="TIME">%1$s</xliff:g>"</string> + <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Լիցքը կարող է սպառվել մինչև <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Մնացել է <xliff:g id="THRESHOLD">%1$s</xliff:g>-ից քիչ"</string> <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Մնացել է <xliff:g id="THRESHOLD">%1$s</xliff:g>-ից քիչ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Մնացել է ավելի քան <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -452,7 +452,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Դանդաղ լիցքավորում"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Չի լիցքավորվում"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Միացված է հոսանքի աղբյուրին, սակայն այս պահին չի կարող լիցքավորվել"</string> + <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Միացված է հոսանքին, այս պահին չի կարող լիցքավորվել"</string> <string name="battery_info_status_full" msgid="4443168946046847468">"Լիցքավորված է"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Վերահսկվում է ադմինիստրատորի կողմից"</string> <string name="disabled" msgid="8017887509554714950">"Կասեցված է"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Անջատված է"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Միացված է"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Սարքն անհրաժեշտ է վերագործարկել, որպեսզի փոփոխությունը կիրառվի։ Վերագործարկեք հիմա կամ չեղարկեք փոփոխությունը։"</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Աշխատանքային <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 3b02fbfcc010..04ca2f69cc61 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -195,7 +195,7 @@ </string-array> <string name="choose_profile" msgid="343803890897657450">"Pilih profil"</string> <string name="category_personal" msgid="6236798763159385225">"Pribadi"</string> - <string name="category_work" msgid="4014193632325996115">"Kantor"</string> + <string name="category_work" msgid="4014193632325996115">"Kerja"</string> <string name="development_settings_title" msgid="140296922921597393">"Opsi developer"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Aktifkan opsi developer"</string> <string name="development_settings_summary" msgid="8718917813868735095">"Menyetel opsi untuk pengembangan apl"</string> @@ -433,7 +433,7 @@ <string name="power_discharge_by" msgid="4113180890060388350">"Akan bertahan kira-kira sampai pukul <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_discharge_by_only" msgid="92545648425937000">"Akan bertahan kira-kira sampai pukul <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Hingga <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Baterai mungkin habis pada <xliff:g id="TIME">%1$s</xliff:g>"</string> + <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Baterai mungkin habis pada pukul <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Tersisa kurang dari <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Tersisa kurang dari <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Tersisa lebih dari <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Nonaktif"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktif"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Perangkat Anda harus di-reboot agar perubahan ini diterapkan. Reboot sekarang atau batalkan."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> kerja"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 6560de0d4a28..ce60eb50031d 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slökkt"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Virkt"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Endurræsa þarf tækið til að þessi breyting taki gildi. Endurræstu núna eða hættu við."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> í vinnu"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index ece0ba489ebc..bef644b60a3b 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Non attivo"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Attivo"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Devi riavviare il dispositivo per applicare questa modifica. Riavvia ora o annulla."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"App <xliff:g id="APP_NAME">%s</xliff:g> di lavoro"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 23e701dfe709..ed796326c708 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -555,6 +555,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"מושבת"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"מופעל"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"צריך להפעיל מחדש את המכשיר כדי להחיל את השינוי. יש להפעיל מחדש עכשיו או לבטל."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> של עבודה"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 764072280202..3f08a6ac83e2 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -210,7 +210,7 @@ <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Wi-Fi 接続時にデバッグモード"</string> <string name="adb_wireless_error" msgid="721958772149779856">"エラー"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"ワイヤレス デバッグ"</string> - <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"利用可能なデバイスを確認して使用するには、ワイヤレス デバッグをオンにしてください"</string> + <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"利用可能なデバイスを確認して使用するには、ワイヤレス デバッグを ON にしてください"</string> <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR コードによるデバイスのペア設定"</string> <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR コードスキャナを使って新しいデバイスをペア設定します"</string> <string name="adb_pair_method_code_title" msgid="1122590300445142904">"ペア設定コードによるデバイスのペア設定"</string> @@ -235,7 +235,7 @@ <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Wi-Fi ネットワークに接続してください"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, デバッグ, dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"バグレポートのショートカット"</string> - <string name="bugreport_in_power_summary" msgid="1885529649381831775">"電源メニューにバグレポートを取得するボタンを表示する"</string> + <string name="bugreport_in_power_summary" msgid="1885529649381831775">"電源ボタン メニューにバグレポートを取得するボタンを表示する"</string> <string name="keep_screen_on" msgid="1187161672348797558">"スリープモードにしない"</string> <string name="keep_screen_on_summary" msgid="1510731514101925829">"充電中に画面をスリープにしない"</string> <string name="bt_hci_snoop_log" msgid="7291287955649081448">"Bluetooth HCI スヌープログ"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"無効"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"有効"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"この変更を適用するには、デバイスの再起動が必要です。今すぐ再起動してください。キャンセルすることもできます。"</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"仕事の<xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 985188fa63d5..2ebc3ee32c38 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ჩართული"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ამ ცვლილების ასამოქმედებლად თქვენი მოწყობილობა უნდა გადაიტვირთოს. გადატვირთეთ ახლავე ან გააუქმეთ."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"სამსახურის <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index a5308e492c28..91e548830ee9 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -42,7 +42,7 @@ <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> арқылы жалғанған"</string> <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s арқылы қолжетімді"</string> <string name="tap_to_sign_up" msgid="5356397741063740395">"Тіркелу үшін түртіңіз."</string> - <string name="wifi_connected_no_internet" msgid="5087420713443350646">"Интернетпен байланыс жоқ."</string> + <string name="wifi_connected_no_internet" msgid="5087420713443350646">"Интернетпен байланыс жоқ"</string> <string name="private_dns_broken" msgid="1984159464346556931">"Жеке DNS серверіне кіру мүмкін емес."</string> <string name="wifi_limited_connection" msgid="1184778285475204682">"Шектеулі байланыс"</string> <string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернетпен байланыс жоқ"</string> @@ -61,7 +61,7 @@ <string name="speed_label_medium" msgid="9078405312828606976">"Орташа"</string> <string name="speed_label_fast" msgid="2677719134596044051">"Жылдам"</string> <string name="speed_label_very_fast" msgid="8215718029533182439">"Өте жылдам"</string> - <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Мерзімі өтті."</string> + <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Мерзімі өтті"</string> <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"Ажыратылған"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Ажыратылуда…"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Өшірулі"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Қосулы"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Бұл өзгеріс күшіне енуі үшін, құрылғыны қайта жүктеу керек. Қазір қайта жүктеңіз не бас тартыңыз."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (жұмыс)"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 0ff48e11f86e..143f5f90a457 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -445,8 +445,8 @@ <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7703677921000858479">"ថេប្លេតអាចនឹងបិទក្នុងពេលបន្តិចទៀត (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string> <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="4374784375644214578">"ឧបករណ៍អាចនឹងបិទក្នុងពេលបន្តិចទៀត (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string> <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបត្រូវសាក"</string> - <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើបត្រូវសាក"</string> + <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបសាកថ្មពេញ"</string> + <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើបសាកថ្មពេញ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"មិនស្គាល់"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"កំពុងបញ្ចូលថ្ម"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string> @@ -537,7 +537,7 @@ <string name="user_setup_dialog_message" msgid="269931619868102841">"សូមប្រាកដថាអ្នកប្រើប្រាស់នេះអាចយកឧបករណ៍ និងរៀបចំទំហំផ្ទុករបស់គេបាន"</string> <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"រៀបចំប្រវត្តិរូបឥឡូវ?"</string> <string name="user_setup_button_setup_now" msgid="1708269547187760639">"រៀបចំឥឡូវ"</string> - <string name="user_setup_button_setup_later" msgid="8712980133555493516">"កុំអាល"</string> + <string name="user_setup_button_setup_later" msgid="8712980133555493516">"កុំទាន់"</string> <string name="user_add_user_type_title" msgid="551279664052914497">"បន្ថែម"</string> <string name="user_new_user_name" msgid="60979820612818840">"អ្នកប្រើថ្មី"</string> <string name="user_new_profile_name" msgid="2405500423304678841">"ប្រវត្តិរូបថ្មី"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"បានបិទ"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"បានបើក"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ត្រូវតែចាប់ផ្ដើមឧបករណ៍របស់អ្នកឡើងវិញ ទើបការផ្លាស់ប្ដូរនេះត្រូវបានអនុវត្ត។ ចាប់ផ្ដើមឡើងវិញឥឡូវនេះ ឬបោះបង់។"</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> សម្រាប់ការងារ"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 52e3b994a424..1df141ca9d9b 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -549,14 +549,11 @@ <string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string> <string name="guest_nickname" msgid="6332276931583337261">"ಅತಿಥಿ"</string> - <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) --> - <skip /> - <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) --> - <skip /> - <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) --> - <skip /> - <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) --> - <skip /> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ಸಾಧನದ ಡೀಫಾಲ್ಟ್"</string> + <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> + <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> + <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ಈ ಬದಲಾವಣೆ ಅನ್ವಯವಾಗಲು ನಿಮ್ಮ ಸಾಧನವನ್ನು ರೀಬೂಟ್ ಮಾಡಬೇಕು. ಇದೀಗ ರೀಬೂಟ್ ಮಾಡಿ ಅಥವಾ ರದ್ದುಗೊಳಿಸಿ."</string> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ಉದ್ಯೋಗ <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index b8d587b9f884..86e165027652 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -495,7 +495,7 @@ <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"시간 줄이기"</string> <string name="cancel" msgid="5665114069455378395">"취소"</string> <string name="okay" msgid="949938843324579502">"확인"</string> - <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"켜기"</string> + <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"사용 설정"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"방해 금지 모드 사용 설정"</string> <string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"사용 안함"</string> <string name="zen_interruption_level_priority" msgid="5392140786447823299">"중요 알림만 허용"</string> @@ -530,7 +530,7 @@ <string name="user_add_profile_item_summary" msgid="5418602404308968028">"내 계정의 앱 및 콘텐츠에 대한 액세스를 제한할 수 있습니다."</string> <string name="user_add_user_item_title" msgid="2394272381086965029">"사용자"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"제한된 프로필"</string> - <string name="user_add_user_title" msgid="5457079143694924885">"새 사용자를 추가할까요?"</string> + <string name="user_add_user_title" msgid="5457079143694924885">"신규 사용자를 추가할까요?"</string> <string name="user_add_user_message_long" msgid="1527434966294733380">"추가 사용자를 만들어 다른 사용자와 기기를 공유할 수 있습니다. 각 사용자는 앱, 배경화면 등으로 맞춤설정할 수 있는 자신만의 공간을 갖게 됩니다. 또한 모든 사용자에게 영향을 미치는 Wi‑Fi와 같은 기기 설정도 조정할 수 있습니다.\n\n추가된 신규 사용자는 자신의 공간을 설정해야 합니다.\n\n모든 사용자가 앱을 업데이트할 수 있으며, 업데이트는 다른 사용자에게도 적용됩니다. 접근성 설정 및 서비스는 신규 사용자에게 이전되지 않을 수도 있습니다."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"추가된 새로운 사용자는 자신의 공간을 설정해야 합니다.\n\n모든 사용자는 다른 사용자들을 위하여 앱을 업데이트할 수 있습니다."</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"지금 사용자를 설정하시겠습니까?"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"사용 중지됨"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"사용 설정됨"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"변경사항을 적용하려면 기기를 재부팅해야 합니다. 지금 재부팅하거나 취소하세요."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"직장용 <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 3c57c790d04a..925f4a706f70 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -224,7 +224,7 @@ <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> туура тармакка туташып турганын текшериңиз"</string> <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Түзмөктү жупташтыруу"</string> <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi жупташтыруучу коду"</string> - <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Жупташтырылган жок"</string> + <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Туташкан жок"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Түзмөк бир тармакка туташып турушу керек."</string> <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR кодун скандап, түзмөктү Wi‑Fi аркылуу жупташтырыңыз"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Түзмөк жупташтырылууда…"</string> @@ -302,7 +302,7 @@ <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Мүмкүнчүлүккө жараша, модем режиминде аппарат тезирээк иштей баштайт"</string> <string name="adb_warning_title" msgid="7708653449506485728">"USB аркылуу жөндөөгө уруксат бересизби?"</string> <string name="adb_warning_message" msgid="8145270656419669221">"USB-жөндөө - өндүрүү максатында гана түзүлгөн. Аны компүтериңиз менен түзмөгүңүздүн ортосунда берилиштерди алмашуу, түзмөгүңүзгө колдонмолорду эскертүүсүз орнотуу жана лог берилиштерин окуу үчүн колдонсоңуз болот."</string> - <string name="adbwifi_warning_title" msgid="727104571653031865">"Мүчүлүштүктөрдү Wi-Fi аркылуу оңдоого уруксат берилсинби?"</string> + <string name="adbwifi_warning_title" msgid="727104571653031865">"Мүчүлүштүктөрдү Wi-Fi аркылуу оңдоого уруксат бересизби?"</string> <string name="adbwifi_warning_message" msgid="8005936574322702388">"Мүчүлүштүктөрдү Wi-Fi аркылуу аныктоо – өндүрүү максатында гана түзүлгөн. Аны компьютериңиз менен түзмөгүңүздүн ортосунда маалыматты алмашуу, колдонмолорду түзмөгүңүзгө эскертүүсүз орнотуу жана маалыматтар таржымалын окуу үчүн колдонсоңуз болот."</string> <string name="adb_keys_warning_message" msgid="2968555274488101220">"Сиз мурун USB жөндөөлөрүнө уруксат берген бардык компүтерлердин жеткиси жокко чыгарылсынбы?"</string> <string name="dev_settings_warning_title" msgid="8251234890169074553">"Жөндөөлөрдү өзгөртүү"</string> @@ -445,8 +445,8 @@ <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7703677921000858479">"Планшет бир аздан кийин өчүп калышы мүмкүн (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string> <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="4374784375644214578">"Түзмөк бир аздан кийин өчүп калышы мүмкүн (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string> <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> кийин кубатталат"</string> - <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> кийин кубатталат"</string> + <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> кийин толук кубатталып бүтөт"</string> + <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> кийин толук кубатталып бүтөт"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Белгисиз"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Кубатталууда"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ыкчам кубатталууда"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Өчүк"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Күйүк"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Бул өзгөртүүнү колдонуу үчүн түзмөктү өчүрүп күйгүзүңүз. Азыр өчүрүп күйгүзүңүз же жокко чыгарыңыз."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Жумуш <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 02f73b50d2b2..3bf1996591f5 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ເປີດການນຳໃຊ້ແລ້ວ"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ທ່ານຕ້ອງປິດເປີດອຸປະກອນຄືນໃໝ່ເພື່ອນຳໃຊ້ການປ່ຽນແປງນີ້. ປິດເປີດໃໝ່ດຽວນີ້ ຫຼື ຍົກເລີກ."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ບ່ອນເຮັດວຽກ <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index dc6347acb4c1..153c9956dc84 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -555,6 +555,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Išjungta"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Įgalinta"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Kad pakeitimas būtų pritaikytas, įrenginį reikia paleisti iš naujo. Dabar paleiskite iš naujo arba atšaukite."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Darbo „<xliff:g id="APP_NAME">%s</xliff:g>“"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 0a185d0d5640..f6e7f35d9211 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -168,7 +168,7 @@ <string name="tts_play_example_summary" msgid="634044730710636383">"Atskaņot īsu runas sintēzes demonstrāciju"</string> <string name="tts_install_data_title" msgid="1829942496472751703">"Instalēt balss datus"</string> <string name="tts_install_data_summary" msgid="3608874324992243851">"Instalēt runas sintēzei nepieciešamos balss datus"</string> - <string name="tts_engine_security_warning" msgid="3372432853837988146">"Lietojot šo runas sintēzes programmu, var tikt apkopots viss ierunātais teksts, tostarp tāda personīgā informācija kā paroles un kredītkaršu numuri. Tā ir no <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> programmas. Vai iespējot šīs runas sintēzes programmas lietošanu?"</string> + <string name="tts_engine_security_warning" msgid="3372432853837988146">"Lietojot šo runas sintēzes programmu, var tikt vākts viss ierunātais teksts, tostarp tāda personīgā informācija kā paroles un kredītkaršu numuri. Tā ir no <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> programmas. Vai iespējot šīs runas sintēzes programmas lietošanu?"</string> <string name="tts_engine_network_required" msgid="8722087649733906851">"Lai izmantotu teksta pārveidošanu runā šajā valodā, ir nepieciešams aktīvs tīkla savienojums."</string> <string name="tts_default_sample_string" msgid="6388016028292967973">"Šis ir runas sintēzes piemērs."</string> <string name="tts_status_title" msgid="8190784181389278640">"Noklusējuma valodas statuss"</string> @@ -554,6 +554,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Atspējots"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Iespējots"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Lai šīs izmaiņas tiktu piemērotas, nepieciešama ierīces atkārtota palaišana. Atkārtoti palaidiet to tūlīt vai atceliet izmaiņas."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Darbā: <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index e0cc38de584a..d55f1aff6f96 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Оневозможено"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Овозможено"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"За да се примени променава, уредот мора да се рестартира. Рестартирајте сега или откажете."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Работна <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 1a3f06830bab..c5267d6b5b0a 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -549,14 +549,11 @@ <string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string> <string name="guest_nickname" msgid="6332276931583337261">"അതിഥി"</string> - <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) --> - <skip /> - <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) --> - <skip /> - <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) --> - <skip /> - <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) --> - <skip /> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ഉപകരണത്തിന്റെ ഡിഫോൾട്ട് പ്രവർത്തനം"</string> + <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"പ്രവർത്തനരഹിതമാക്കി"</string> + <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"പ്രവർത്തനക്ഷമമാക്കി"</string> + <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ഈ മാറ്റം ബാധകമാകുന്നതിന് നിങ്ങളുടെ ഉപകരണം റീബൂട്ട് ചെയ്യേണ്ടതുണ്ട്. ഇപ്പോൾ റീബൂട്ട് ചെയ്യുകയോ റദ്ദാക്കുകയോ ചെയ്യുക."</string> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ഔദ്യോഗികം <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index a342862afe59..0a01f8480d0c 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -419,7 +419,7 @@ <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномаль (улаан-ногоон)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомаль (цэнхэр-шар)"</string> <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Өнгө тохируулах"</string> - <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"Өнгө залруулга нь төхөөрөмж дээрээ өнгийг хэрхэн үзүүлэхийг тохируулах боломжийг танд олгодог"</string> + <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"Өнгө тохируулга нь танд төхөөрөмж дээрээ өнгө хэрхэн харагдахыг тохируулах боломжийг олгодог"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ойролцоогоор <xliff:g id="TIME_REMAINING">%1$s</xliff:g> үлдсэн"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Идэвхгүй болгосон"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Идэвхжүүлсэн"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Энэ өөрчлөлтийг хэрэгжүүлэхийн тулд таны төхөөрөмжийг дахин асаах ёстой. Одоо дахин асаах эсвэл болино уу."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Ажлын <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 0895cd4dfdaf..075c748f1bb7 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -243,7 +243,7 @@ <string name="oem_unlock_enable" msgid="5334869171871566731">"OEM अनलॉक करणे"</string> <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"बूटलोडर अनलॉक करण्यासाठी अनुमती द्या"</string> <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"OEM अनलॉक करण्यास अनुमती द्यायची?"</string> - <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"चेतावणी: हे सेटिंग चालू असताना या डिव्हाइस वर डिव्हाइस संरक्षण वैशिष्ट्ये काम करणार नाहीत."</string> + <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"चेतावणी: हे सेटिंग सुरू असताना या डिव्हाइस वर डिव्हाइस संरक्षण वैशिष्ट्ये काम करणार नाहीत."</string> <string name="mock_location_app" msgid="6269380172542248304">"बनावट स्थान अॅप निवडा"</string> <string name="mock_location_app_not_set" msgid="6972032787262831155">"कोणताही बनावट स्थान अॅप सेट केला नाही"</string> <string name="mock_location_app_set" msgid="4706722469342913843">"बनावट स्थान अॅप: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -298,7 +298,7 @@ <string name="allow_mock_location" msgid="2102650981552527884">"बनावट स्थानांना अनुमती द्या"</string> <string name="allow_mock_location_summary" msgid="179780881081354579">"बनावट स्थानांना अनुमती द्या"</string> <string name="debug_view_attributes" msgid="3539609843984208216">"दृश्य विशेषता तपासणी सुरू करा"</string> - <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"वाय-फाय चालू असतानाही मोबाइल डेटा नेहमी सुरू ठेवा (नेटवर्क जलदरीत्या स्विच करण्यासाठी)."</string> + <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"वाय-फाय सुरू असतानाही मोबाइल डेटा नेहमी सुरू ठेवा (नेटवर्क जलदरीत्या स्विच करण्यासाठी)."</string> <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"उपलब्ध असल्यास टेदरिंग हार्डवेअर ॲक्सिलरेशन वापरा"</string> <string name="adb_warning_title" msgid="7708653449506485728">"USB डीबग करण्यास अनुमती द्यायची?"</string> <string name="adb_warning_message" msgid="8145270656419669221">"USB डीबग करण्याचा हेतू फक्त विकास उद्देशांसाठी आहे. याचा वापर तुमचा कॉंप्युटर आणि तुमचे डिव्हाइस यांच्या दरम्यान डेटा कॉपी करण्यासाठी करा, सूचनेशिवाय तुमच्या डिव्हाइस वर अॅप्स इंस्टॉल करा आणि लॉग डेटा वाचा."</string> @@ -409,7 +409,7 @@ <string name="convert_to_file_encryption_enabled" msgid="840757431284311754">"रूपांतरित करा..."</string> <string name="convert_to_file_encryption_done" msgid="8965831011811180627">"फाईल आधीपासून एंक्रिप्ट होती"</string> <string name="title_convert_fbe" msgid="5780013350366495149">"फाईल आधारित कूटबद्धीकरणावर रूपांतरित करणे"</string> - <string name="convert_to_fbe_warning" msgid="34294381569282109">"फाईल आधारित कूटबद्धीकरणावर डेटा विभाजक रूपांतरित करा.\n !!चेतावणी!! हे आपल्या सर्व डेटास मिटवेल.\n हे वैशिष्ट्य अल्फा आहे आणि कदाचित योग्यरित्या कार्य करू शकत नाही.\n सुरु ठेवण्यासाठी \'पुसा आणि रूपांतरित करा...\' दाबा."</string> + <string name="convert_to_fbe_warning" msgid="34294381569282109">"फाईल आधारित कूटबद्धीकरणावर डेटा विभाजक रूपांतरित करा.\n !!चेतावणी!! हे आपल्या सर्व डेटास मिटवेल.\n हे वैशिष्ट्य अल्फा आहे आणि कदाचित योग्यरित्या कार्य करू शकत नाही.\n सुरू ठेवण्यासाठी \'पुसा आणि रूपांतरित करा...\' दाबा."</string> <string name="button_convert_fbe" msgid="1159861795137727671">"पुसा आणि रुपांतरित करा..."</string> <string name="picture_color_mode" msgid="1013807330552931903">"चित्र रंग मोड"</string> <string name="picture_color_mode_desc" msgid="151780973768136200">"sRGB वापरा"</string> @@ -495,8 +495,8 @@ <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"कमी वेळ."</string> <string name="cancel" msgid="5665114069455378395">"रद्द करा"</string> <string name="okay" msgid="949938843324579502">"ठीक आहे"</string> - <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"चालू करा"</string> - <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"व्यत्यय आणू नका चालू करा"</string> + <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"सुरू करा"</string> + <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"व्यत्यय आणू नका सुरू करा"</string> <string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"कधीही नाही"</string> <string name="zen_interruption_level_priority" msgid="5392140786447823299">"केवळ प्राधान्य"</string> <string name="zen_mode_and_condition" msgid="8877086090066332516">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string> @@ -549,14 +549,11 @@ <string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string> <string name="guest_nickname" msgid="6332276931583337261">"अतिथी"</string> - <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) --> - <skip /> - <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) --> - <skip /> - <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) --> - <skip /> - <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) --> - <skip /> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"डिव्हाइस डीफॉल्ट"</string> + <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"बंद केले आहे"</string> + <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सुरू केले आहे"</string> + <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"हा बदल लागू करण्यासाठी तुमचे डिव्हाइस रीबूट करणे आवश्यक आहे. आता रीबूट करा किंवा रद्द करा."</string> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"कार्य <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index a1c67826b69c..cdf32a7b39ce 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Dilumpuhkan"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Didayakan"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Peranti anda mesti dibut semula supaya perubahan ini berlaku. But semula sekarang atau batalkan."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Kerja <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 0edc10073e70..2bd3b450238f 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -153,7 +153,7 @@ <string name="unknown" msgid="3544487229740637809">"မသိ"</string> <string name="running_process_item_user_label" msgid="3988506293099805796">"အသုံးပြုသူ- <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> <string name="launch_defaults_some" msgid="3631650616557252926">"မူရင်းအချို့ သတ်မှတ်ပြီး"</string> - <string name="launch_defaults_none" msgid="8049374306261262709">"ပုံမှန်သတ်မှတ်ထားခြင်းမရှိ"</string> + <string name="launch_defaults_none" msgid="8049374306261262709">"မူရင်း သတ်မှတ်မထားပါ။"</string> <string name="tts_settings" msgid="8130616705989351312">"စာသားမှစကားပြောပြောင်း ဆက်တင်များ"</string> <string name="tts_settings_title" msgid="7602210956640483039">"စာသားမှ စကားပြောသို့ အထွက်"</string> <string name="tts_default_rate_title" msgid="3964187817364304022">"စကားပြောနှုန်း"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ပိတ်ထားသည်"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ဖွင့်ထားသည်"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ဤအပြောင်းအလဲ ထည့်သွင်းရန် သင့်စက်ကို ပြန်လည်စတင်ရမည်။ ယခု ပြန်လည်စတင်ပါ သို့မဟုတ် ပယ်ဖျက်ပါ။"</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"အလုပ် <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index d6830700338a..de5bed63b70a 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slått av"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Slått på"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Enheten din må startes på nytt for at denne endringen skal tre i kraft. Start på nytt nå eller avbryt."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Jobb-<xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index ee22c60dc0c2..bfe295a4066a 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -399,7 +399,7 @@ <string name="inactive_apps_title" msgid="5372523625297212320">"स्ट्यान्डबाई एपहरू"</string> <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"निष्क्रिय। टगल गर्न ट्याप गर्नुहोस्।"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"सक्रिय। टगल गर्न ट्याप गर्नुहोस्।"</string> - <string name="standby_bucket_summary" msgid="5128193447550429600">"अनुप्रयोगको स्ट्यान्डबाई अवस्था:<xliff:g id="BUCKET"> %s</xliff:g>"</string> + <string name="standby_bucket_summary" msgid="5128193447550429600">"एपको स्ट्यान्डबाई अवस्था:<xliff:g id="BUCKET"> %s</xliff:g>"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"चलिरहेका सेवाहरू"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"हाल चालु भइरहेका सेवाहरू हेर्नुहोस् र नियन्त्रण गर्नुहोस्"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView कार्यान्वयन"</string> @@ -549,14 +549,11 @@ <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि थप्नुहोस्"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथि हटाउनुहोस्"</string> <string name="guest_nickname" msgid="6332276931583337261">"अतिथि"</string> - <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) --> - <skip /> - <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) --> - <skip /> - <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) --> - <skip /> - <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) --> - <skip /> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"पूर्वनिर्धारित यन्त्र"</string> + <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"असक्षम पारिएको छ"</string> + <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सक्षम पारिएको छ"</string> + <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"यो परिवर्तन लागू गर्न तपाईंको यन्त्र अनिवार्य रूपमा रिबुट गर्नु पर्छ। अहिले रिबुट गर्नुहोस् वा रद्द गर्नुहोस्।"</string> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"कार्यालयको प्रोफाइल <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index dc5f8f2909c1..a82271981fca 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ingeschakeld"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Je apparaat moet opnieuw worden opgestart om deze wijziging toe te passen. Start nu opnieuw op of annuleer de wijziging."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> voor werk"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 18cc9e3bd382..ab9fbc364f43 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -235,7 +235,7 @@ <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"ଦୟାକରି ଏକ ୱାଇ-ଫାଇ ନେଟୱାର୍କରେ ସଂଯୋଗ କରନ୍ତୁ"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, ଡିବଗ୍, dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"ବଗ୍ ରିପୋର୍ଟ ସର୍ଟକଟ୍"</string> - <string name="bugreport_in_power_summary" msgid="1885529649381831775">"ବଗ୍ ରିପୋର୍ଟ ଦେବାପାଇଁ ପାୱାର୍ ମେନୁରେ ଏକ ବଟନ୍ ଦେଖନ୍ତୁ"</string> + <string name="bugreport_in_power_summary" msgid="1885529649381831775">"ବଗ୍ ରିପୋର୍ଟ ଦେବା ପାଇଁ ପାୱାର୍ ମେନୁରେ ଏକ ବଟନ୍ ଦେଖାନ୍ତୁ"</string> <string name="keep_screen_on" msgid="1187161672348797558">"ଜାଗ୍ରତ ରଖନ୍ତୁ"</string> <string name="keep_screen_on_summary" msgid="1510731514101925829">"ଚାର୍ଜ ହେବାବେଳେ ସ୍କ୍ରୀନ୍ ଆଦୌ ବନ୍ଦ ହେବନାହିଁ"</string> <string name="bt_hci_snoop_log" msgid="7291287955649081448">"ବ୍ଲୁଟୂଥ୍ HCI ସ୍ନୁପ୍ ଲଗ୍ ସକ୍ଷମ କରନ୍ତୁ"</string> @@ -245,7 +245,7 @@ <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"OEM ଅନଲକ୍ କରିବା ଅନୁମତି ଦେବେ?"</string> <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"ଚେତାବନୀ: ଏହି ସେଟିଙ୍ଗ ଚାଲୁ ଥିବାବେଳେ ଡିଭାଇସ୍ର ସୁରକ୍ଷା ବୈଶିଷ୍ଟ୍ୟ କାମ କରିବ ନାହିଁ"</string> <string name="mock_location_app" msgid="6269380172542248304">"ମକ୍ ଲୋକେସନ୍ ଆପ୍ର ଚୟନ କରନ୍ତୁ"</string> - <string name="mock_location_app_not_set" msgid="6972032787262831155">"କୌଣସି ନକଲି ଲୋକେଶନ ଆପ୍ ସେଟ୍ କରାଯାଇନାହିଁ"</string> + <string name="mock_location_app_not_set" msgid="6972032787262831155">"କୌଣସି ମକ୍ ଲୋକେସନ ଆପ୍ ସେଟ୍ କରାଯାଇନାହିଁ"</string> <string name="mock_location_app_set" msgid="4706722469342913843">"ମକ୍ ଲୋକେସନ୍ ଆପ୍: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="debug_networking_category" msgid="6829757985772659599">"ନେଟ୍ୱର୍କିଙ୍ଗ"</string> <string name="wifi_display_certification" msgid="1805579519992520381">"ୱାୟରଲେସ୍ ଡିସ୍ପ୍ଲେ ସାର୍ଟିଫିକେସନ୍"</string> @@ -549,14 +549,11 @@ <string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string> <string name="guest_nickname" msgid="6332276931583337261">"ଅତିଥି"</string> - <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) --> - <skip /> - <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) --> - <skip /> - <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) --> - <skip /> - <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) --> - <skip /> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ଡିଭାଇସ୍ ଡିଫଲ୍ଟ"</string> + <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ଅକ୍ଷମ କରାଯାଇଛି"</string> + <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ସକ୍ଷମ କରାଯାଇଛି"</string> + <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ଏହି ପରିବର୍ତ୍ତନ ଲାଗୁ କରିବା ପାଇଁ ଆପଣଙ୍କ ଡିଭାଇସକୁ ନିଶ୍ଚିତ ରୂପେ ରିବୁଟ୍ କରାଯିବା ଆବଶ୍ୟକ। ବର୍ତ୍ତମାନ ରିବୁଟ୍ କରନ୍ତୁ କିମ୍ବା ବାତିଲ୍ କରନ୍ତୁ।"</string> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ୱାର୍କ <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index b502f3fa25e6..35d8cbab0b36 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -549,14 +549,11 @@ <string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string> <string name="guest_nickname" msgid="6332276931583337261">"ਮਹਿਮਾਨ"</string> - <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) --> - <skip /> - <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) --> - <skip /> - <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) --> - <skip /> - <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) --> - <skip /> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਡੀਵਾਈਸ"</string> + <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ਬੰਦ ਕੀਤਾ ਗਿਆ"</string> + <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string> + <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ਇਸ ਤਬਦੀਲੀ ਨੂੰ ਲਾਗੂ ਕਰਨ ਲਈ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨੂੰ ਰੀਬੂਟ ਕਰਨਾ ਲਾਜ਼ਮੀ ਹੈ। ਹੁਣੇ ਰੀਬੂਟ ਕਰੋ ਜਾਂ ਰੱਦ ਕਰੋ।"</string> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ਕੰਮ <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 8a29bf0f4431..54ed131797a9 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -195,7 +195,7 @@ </string-array> <string name="choose_profile" msgid="343803890897657450">"Wybierz profil"</string> <string name="category_personal" msgid="6236798763159385225">"Osobiste"</string> - <string name="category_work" msgid="4014193632325996115">"Do pracy"</string> + <string name="category_work" msgid="4014193632325996115">"Służbowe"</string> <string name="development_settings_title" msgid="140296922921597393">"Opcje programistyczne"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Włącz opcje dla programistów"</string> <string name="development_settings_summary" msgid="8718917813868735095">"Ustaw opcje związane z programowaniem aplikacji."</string> @@ -555,6 +555,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Wyłączono"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Włączono"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Wprowadzenie zmiany wymaga ponownego uruchomienia urządzenia. Uruchom ponownie teraz lub anuluj."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (do pracy)"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 58a13cdd1999..c6dc1d3376b9 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -61,7 +61,7 @@ <string name="speed_label_medium" msgid="9078405312828606976">"Média"</string> <string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string> <string name="speed_label_very_fast" msgid="8215718029533182439">"Muito rápida"</string> - <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirado"</string> + <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirada"</string> <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconectado"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Desconectando…"</string> @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"App <xliff:g id="APP_NAME">%s</xliff:g> de trabalho"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index ca851516bec9..999e684f124d 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -212,9 +212,9 @@ <string name="adb_wireless_settings" msgid="2295017847215680229">"Depuração sem fios"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Para ver e utilizar dispositivos disponíveis, ative a depuração sem fios."</string> <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Sincronize o dispositivo com o código QR"</string> - <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Sincronize novos dispositivos com o leitor de códigos QR."</string> + <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Sincroniza novos dispositivos com o leitor de códigos QR."</string> <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Sincronize dispositivo com código de sincronização"</string> - <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Sincronize novos dispositivos através do código de seis dígitos."</string> + <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Sincroniza novos dispositivos através do código de seis dígitos."</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"Dispositivos sincronizados"</string> <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Atualmente ligado."</string> <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Detalhes do dispositivo"</string> @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativada"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reiniciar o dispositivo para aplicar esta alteração. Reinicie agora ou cancele."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> de trabalho"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 58a13cdd1999..c6dc1d3376b9 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -61,7 +61,7 @@ <string name="speed_label_medium" msgid="9078405312828606976">"Média"</string> <string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string> <string name="speed_label_very_fast" msgid="8215718029533182439">"Muito rápida"</string> - <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirado"</string> + <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirada"</string> <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconectado"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Desconectando…"</string> @@ -554,4 +554,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"App <xliff:g id="APP_NAME">%s</xliff:g> de trabalho"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index f967c8ca8d6b..ba2f36fc5c1d 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -554,6 +554,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Dezactivat"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pentru ca modificarea să se aplice, trebuie să reporniți dispozitivul. Reporniți-l acum sau anulați."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> de serviciu"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index e41b3b7cf587..8ec387584c5a 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -210,7 +210,7 @@ <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Режим отладки при подключении к сети Wi‑Fi"</string> <string name="adb_wireless_error" msgid="721958772149779856">"Ошибка"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"Отладка по Wi-Fi"</string> - <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Чтобы посмотреть и использовать доступные устройства, включите отладку по Wi-Fi"</string> + <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Чтобы увидеть и использовать доступные устройства, включите отладку по Wi-Fi."</string> <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Подключить устройство с помощью QR-кода"</string> <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Подключение новых устройств с помощью сканера QR-кодов"</string> <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Подключить устройство с помощью кода подключения"</string> @@ -223,7 +223,7 @@ <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Не удалось установить подключение"</string> <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Убедитесь, что устройство \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" подключено к нужной сети."</string> <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Подключение к устройству"</string> - <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Код подключения к сети Wi‑Fi"</string> + <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Код подключения по сети Wi‑Fi"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Не удалось подключить устройство"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Устройство должно быть подключено к той же самой сети."</string> <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Подключение устройства через Wi‑Fi с использованием QR-кода"</string> @@ -555,6 +555,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Отключено"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Включено"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Чтобы изменение вступило в силу, необходимо перезапустить устройство. Вы можете сделать это сейчас или позже."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Рабочее приложение \"<xliff:g id="APP_NAME">%s</xliff:g>\""</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 571098554562..01c36349bb8f 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"අබල කළා"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"සබලයි"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"මෙම වෙනස යෙදීමට ඔබේ උපාංගය නැවත පණ ගැන්විය යුතුය. දැන් නැවත පණ ගන්වන්න හෝ අවලංගු කරන්න."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"කාර්යාල <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index b3848a9dfc11..b5cbf435f356 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -61,7 +61,7 @@ <string name="speed_label_medium" msgid="9078405312828606976">"Stredná"</string> <string name="speed_label_fast" msgid="2677719134596044051">"Rýchla"</string> <string name="speed_label_very_fast" msgid="8215718029533182439">"Veľmi rýchla"</string> - <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Platnosť vypršala"</string> + <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Vypršalo"</string> <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"Odpojený"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Prebieha odpájanie..."</string> @@ -195,7 +195,7 @@ </string-array> <string name="choose_profile" msgid="343803890897657450">"Výber profilu"</string> <string name="category_personal" msgid="6236798763159385225">"Osobné"</string> - <string name="category_work" msgid="4014193632325996115">"Práca"</string> + <string name="category_work" msgid="4014193632325996115">"Pracovné"</string> <string name="development_settings_title" msgid="140296922921597393">"Pre vývojárov"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Povolenie možností vývojára"</string> <string name="development_settings_summary" msgid="8718917813868735095">"Možnosti nastavenia vývoja aplikácií"</string> @@ -508,7 +508,7 @@ <string name="alarm_template_far" msgid="6382760514842998629">"o <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Trvanie"</string> <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vždy sa opýtať"</string> - <string name="zen_mode_forever" msgid="3339224497605461291">"Dokiaľ túto funkciu nevypnete"</string> + <string name="zen_mode_forever" msgid="3339224497605461291">"Dokým funkciu nevypnete"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Teraz"</string> <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Reproduktor telefónu"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Pri pripájaní sa vyskytol problém. Zariadenie vypnite a znova zapnite."</string> @@ -555,6 +555,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuté"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuté"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Táto zmena sa uplatní až po reštartovaní zariadenia. Zariadenie reštartujte alebo zmenu zrušte."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Pracovná aplikácia <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 658c1923656d..86cccb859e27 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -214,7 +214,7 @@ <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Seznanjanje naprave s kodo QR"</string> <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Seznanitev novih naprav z optičnim bralnikom kod QR"</string> <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Seznanjanje naprave s kodo za seznanjanje"</string> - <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Seznanjanje novih naprav s šestmestno kodo"</string> + <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Seznanitev novih naprav s šestmestno kodo"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"Seznanjene naprave"</string> <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Trenutno povezano"</string> <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Podrobnosti o napravi"</string> @@ -509,7 +509,7 @@ <string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Trajanje"</string> <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vedno vprašaj"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dokler ne izklopite"</string> - <string name="time_unit_just_now" msgid="3006134267292728099">"pravkar"</string> + <string name="time_unit_just_now" msgid="3006134267292728099">"Pravkar"</string> <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvočnik telefona"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Težava pri povezovanju. Napravo izklopite in znova vklopite."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žična zvočna naprava"</string> @@ -556,4 +556,6 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogočeno"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Napravo je treba znova zagnati, da bo ta sprememba uveljavljena. Znova zaženite zdaj ali prekličite."</string> <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> za delo"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> + <skip /> </resources> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 17d2d502d386..bd3353cfc2d3 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -207,7 +207,7 @@ <string name="enable_adb_summary" msgid="3711526030096574316">"Korrigjo gabimet e modalitetit kur UBS-ja është e lidhur"</string> <string name="clear_adb_keys" msgid="3010148733140369917">"Anulo autorizimet e korrigjimeve të gabimeve të USB-së"</string> <string name="enable_adb_wireless" msgid="6973226350963971018">"Korrigjimi përmes Wi-Fi"</string> - <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Regjimi i korrigjimit kur Wi‑Fi është i lidhur"</string> + <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Modaliteti i korrigjimit kur Wi‑Fi është i lidhur"</string> <string name="adb_wireless_error" msgid="721958772149779856">"Gabim"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"Korrigjimi përmes Wi-Fi"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Për të parë dhe përdorur pajisjet e disponueshme, aktivizo korrigjimin përmes Wi-Fi"</string> @@ -446,7 +446,7 @@ <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="4374784375644214578">"Pajisja mund të fiket së shpejti (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string> <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura deri në karikim"</string> - <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të karikohen"</string> + <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të karikohet"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"I panjohur"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Po karikohet"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Po ngarkon me shpejtësi"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Joaktiv"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pajisja jote duhet të riniset që ky ndryshim të zbatohet. Rinise tani ose anuloje."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> për punën"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index e0a7d0b28ccd..29f23b48d5e0 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -554,6 +554,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Онемогућено"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Омогућено"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Морате да рестартујете уређај да би се ова промена применила. Рестартујте га одмах или откажите."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> за посао"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index f6745b2ebf3c..a40100e83f51 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -183,19 +183,19 @@ <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Återställ tonhöjden för tal"</string> <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Återställ tonhöjden för talad text till standardinställningen."</string> <string-array name="tts_rate_entries"> - <item msgid="9004239613505400644">"Mycket långsamt"</item> - <item msgid="1815382991399815061">"Långsamt"</item> - <item msgid="3075292553049300105">"Normalt"</item> - <item msgid="1158955023692670059">"Snabbt"</item> + <item msgid="9004239613505400644">"Mycket långsam"</item> + <item msgid="1815382991399815061">"Långsam"</item> + <item msgid="3075292553049300105">"Normal"</item> + <item msgid="1158955023692670059">"Snabb"</item> <item msgid="5664310435707146591">"Snabbare"</item> - <item msgid="5491266922147715962">"Mycket snabbt"</item> - <item msgid="7659240015901486196">"Supersnabbt"</item> - <item msgid="7147051179282410945">"Turbosnabbt"</item> + <item msgid="5491266922147715962">"Mycket snabb"</item> + <item msgid="7659240015901486196">"Supersnabb"</item> + <item msgid="7147051179282410945">"Turbosnabb"</item> <item msgid="581904787661470707">"Snabbast"</item> </string-array> <string name="choose_profile" msgid="343803890897657450">"Välj profil"</string> - <string name="category_personal" msgid="6236798763159385225">"Personligt"</string> - <string name="category_work" msgid="4014193632325996115">"Arbetet"</string> + <string name="category_personal" msgid="6236798763159385225">"Privat"</string> + <string name="category_work" msgid="4014193632325996115">"Jobb"</string> <string name="development_settings_title" msgid="140296922921597393">"Utvecklaralternativ"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Aktivera utvecklaralternativ"</string> <string name="development_settings_summary" msgid="8718917813868735095">"Ange alternativ för apputveckling"</string> @@ -213,9 +213,9 @@ <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Aktivera trådlös felsökning om du vill se tillgängliga enheter"</string> <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Parkoppla enheten med en QR-kod"</string> <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Parkoppla nya enheter med QR-kodsläsare"</string> - <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Koppla enheten med en kopplingskod"</string> - <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Koppla nya enheter med en sexsiffrig kod"</string> - <string name="adb_paired_devices_title" msgid="5268997341526217362">"Kopplade enheter"</string> + <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Parkoppla enheten med en parkopplingskod"</string> + <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Parkoppla nya enheter med en sexsiffrig kod"</string> + <string name="adb_paired_devices_title" msgid="5268997341526217362">"Parkopplade enheter"</string> <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Anslutna just nu"</string> <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Enhetsinformation"</string> <string name="adb_device_forget" msgid="193072400783068417">"Glöm"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inaktiverat"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiverat"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Enheten måste startas om för att ändringen ska börja gälla. Starta om nu eller avbryt."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> för arbetet"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml index a29b74e6d17d..af3935694cdc 100644 --- a/packages/SettingsLib/res/values-sw/arrays.xml +++ b/packages/SettingsLib/res/values-sw/arrays.xml @@ -40,7 +40,7 @@ <item msgid="8339720953594087771">"Inaunganisha kwa <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item> <item msgid="3028983857109369308">"Uhalalishaji kwa <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item> <item msgid="4287401332778341890">"Inamiliki anwani ya IP kutoka <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item> - <item msgid="1043944043827424501">" Umeunganishwa kwa<xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item> + <item msgid="1043944043827424501">"Umeunganishwa kwa <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item> <item msgid="7445993821842009653">"Imesimamishwa"</item> <item msgid="1175040558087735707">"inakatisha muunganisho kutoka <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item> <item msgid="699832486578171722">"Muunganisho Umekatika"</item> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 7df39d5ded33..fedf1c85946c 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -195,7 +195,7 @@ </string-array> <string name="choose_profile" msgid="343803890897657450">"Chagua wasifu"</string> <string name="category_personal" msgid="6236798763159385225">"Ya Kibinafsi"</string> - <string name="category_work" msgid="4014193632325996115">"Kazini"</string> + <string name="category_work" msgid="4014193632325996115">"Ya Kazini"</string> <string name="development_settings_title" msgid="140296922921597393">"Chaguo za wasanidi"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Washa chaguo za wasanidi programu"</string> <string name="development_settings_summary" msgid="8718917813868735095">"Weka chaguo kwa ajili ya maendeleo ya programu"</string> @@ -485,7 +485,7 @@ <string name="ims_reg_title" msgid="8197592958123671062">"Hali ya usajili wa IMS"</string> <string name="ims_reg_status_registered" msgid="884916398194885457">"Imesajiliwa"</string> <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"Haijasajiliwa"</string> - <string name="status_unavailable" msgid="5279036186589861608">"Haipatikani"</string> + <string name="status_unavailable" msgid="5279036186589861608">"Hamna"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Imechagua anwani ya MAC kwa nasibu"</string> <plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139"> <item quantity="other">Imeunganisha vifaa %1$d</item> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Imezimwa"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Imewashwa"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Ni lazima uwashe tena kifaa chako ili mabadiliko haya yatekelezwe. Washa tena sasa au ughairi."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Ya kazini <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index fa922ea8804b..345f0a7c560d 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"முடக்கப்பட்டது"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"இயக்கப்பட்டது"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"இந்த மாற்றங்கள் செயல்படுத்தப்பட உங்கள் சாதனத்தை மறுபடி தொடங்க வேண்டும். இப்போதே மறுபடி தொடங்கவும் அல்லது ரத்துசெய்யவும்."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"பணியிடம் <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 0e36c5fe53cf..c4fda01c19d2 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -153,9 +153,9 @@ <string name="unknown" msgid="3544487229740637809">"తెలియదు"</string> <string name="running_process_item_user_label" msgid="3988506293099805796">"వినియోగదారు: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> <string name="launch_defaults_some" msgid="3631650616557252926">"కొన్ని డిఫాల్ట్లు సెట్ చేయబడ్డాయి"</string> - <string name="launch_defaults_none" msgid="8049374306261262709">"ఆటోమేటిక్ ఆప్షన్లు వేటినీ సెట్ చేయలేదు"</string> + <string name="launch_defaults_none" msgid="8049374306261262709">"ఆటోమేటిక్ ఆప్షన్లు ఏవీ సెట్ చేయలేదు"</string> <string name="tts_settings" msgid="8130616705989351312">"వచనం నుండి ప్రసంగం సెట్టింగ్లు"</string> - <string name="tts_settings_title" msgid="7602210956640483039">"వచనం నుండి మాట అవుట్పుట్"</string> + <string name="tts_settings_title" msgid="7602210956640483039">"టెక్స్ట్-టు-స్పీచ్ అవుట్పుట్"</string> <string name="tts_default_rate_title" msgid="3964187817364304022">"ప్రసంగం రేట్"</string> <string name="tts_default_rate_summary" msgid="3781937042151716987">"వచనాన్ని చదివి వినిపించాల్సిన వేగం"</string> <string name="tts_default_pitch_title" msgid="6988592215554485479">"పిచ్"</string> @@ -195,7 +195,7 @@ </string-array> <string name="choose_profile" msgid="343803890897657450">"ప్రొఫైల్ను ఎంచుకోండి"</string> <string name="category_personal" msgid="6236798763159385225">"వ్యక్తిగతం"</string> - <string name="category_work" msgid="4014193632325996115">"కార్యాలయం"</string> + <string name="category_work" msgid="4014193632325996115">"ఆఫీస్"</string> <string name="development_settings_title" msgid="140296922921597393">"డెవలపర్ ఎంపికలు"</string> <string name="development_settings_enable" msgid="4285094651288242183">"డెవలపర్ ఎంపికలను ప్రారంభించండి"</string> <string name="development_settings_summary" msgid="8718917813868735095">"అనువర్తన అభివృద్ధి కోసం ఎంపికలను సెట్ చేయండి"</string> @@ -229,7 +229,7 @@ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR కోడ్ను స్కాన్ చేయడం ద్వారా Wi-Fiని ఉపయోగించి పరికరాన్ని పెయిర్ చెయ్యండి"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"పరికరం పెయిర్ చేయబడుతోంది…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"పరికరాన్ని పెయిర్ చేయడం విఫలమైంది. QR కోడ్ తప్పుగా ఉండడం గాని, లేదా పరికరం అదే నెట్వర్క్కు కనెక్ట్ అయి లేకపోవడం గాని జరిగింది."</string> - <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP చిరునామా & పోర్ట్"</string> + <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP అడ్రస్ & పోర్ట్"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR కోడ్ను స్కాన్ చేయండి"</string> <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR కోడ్ను స్కాన్ చేయడం ద్వారా Wi-Fiని ఉపయోగించి పరికరాన్ని పెయిర్ చెయ్యండి"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"దయచేసి Wi-Fi నెట్వర్క్కు కనెక్ట్ చేయండి"</string> @@ -418,8 +418,8 @@ <string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"డ్యూటెరానోమలీ (ఎరుపు-ఆకుపచ్చ)"</string> <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ప్రొటానోమలీ (ఎరుపు-ఆకుపచ్చ రంగు)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ట్రైటనోమలీ (నీలం-పసుపు రంగు)"</string> - <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"రంగు సవరణ"</string> - <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"రంగులు సరి చేసే ఫీచర్ సాయంతో, మీ పరికరంలో రంగులు కనిపించే పద్ధతిని మీరు సర్దుబాటు చేయగలుగుతారు"</string> + <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"కలర్ సరిచేయడం"</string> + <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"\'కలర్ సరిచేయడం\' అనే ఫీచర్ సాయంతో, మీ పరికరంలో రంగులు కనిపించే పద్ధతిని మీరు మార్చగలుగుతారు"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string> @@ -533,7 +533,7 @@ <string name="user_add_user_title" msgid="5457079143694924885">"కొత్త వినియోగదారుని జోడించాలా?"</string> <string name="user_add_user_message_long" msgid="1527434966294733380">"అదనపు యూజర్లను సృష్టించడం ద్వారా మీరు ఈ దేవైజ్ను ఇతరులతో షేర్ చేయవచ్చు. ప్రతి యూజర్కు వారికంటూ ప్రత్యేక స్థలం ఉంటుంది, వారు ఆ స్థలాన్ని యాప్లు, వాల్పేపర్ మొదలైనవాటితో అనుకూలీకరించవచ్చు. యూజర్లు ప్రతి ఒక్కరిపై ప్రభావం చూపే Wi‑Fi వంటి పరికర సెట్టింగ్లను కూడా సర్దుబాటు చేయవచ్చు.\n\nమీరు కొత్త యూజర్ ను జోడించినప్పుడు, ఆ వ్యక్తి వారికంటూ స్వంత స్థలం సెట్ చేసుకోవాలి.\n\nఏ వినియోగదారు అయినా మిగిలిన అందరు యూజర్ల కోసం యాప్లను అప్డేట్ చేయవచ్చు. యాక్సెస్ సామర్ధ్యం సెట్టింగ్లు మరియు సేవలు కొత్త యూజర్కి బదిలీ కాకపోవచ్చు."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"మీరు కొత్త వినియోగదారుని జోడించినప్పుడు, ఆ వ్యక్తి తన స్థలాన్ని సెటప్ చేసుకోవాలి.\n\nఏ వినియోగదారు అయినా మిగతా అందరు వినియోగదారుల కోసం యాప్లను అప్డేట్ చేయగలరు."</string> - <string name="user_setup_dialog_title" msgid="8037342066381939995">"ఇప్పుడు వినియోగదారుని సెటప్ చేయాలా?"</string> + <string name="user_setup_dialog_title" msgid="8037342066381939995">"యూజర్ను ఇప్పుడే సెటప్ చేయాలా?"</string> <string name="user_setup_dialog_message" msgid="269931619868102841">"పరికరాన్ని తీసుకోవడానికి వ్యక్తి అందుబాటులో ఉన్నారని నిర్ధారించుకొని, ఆపై వారికి నిల్వ స్థలాన్ని సెటప్ చేయండి"</string> <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"ఇప్పుడు ప్రొఫైల్ను సెటప్ చేయాలా?"</string> <string name="user_setup_button_setup_now" msgid="1708269547187760639">"ఇప్పుడే సెటప్ చేయి"</string> @@ -549,14 +549,11 @@ <string name="guest_new_guest" msgid="3482026122932643557">"అతిథిని జోడించండి"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"అతిథిని తీసివేయండి"</string> <string name="guest_nickname" msgid="6332276931583337261">"అతిథి"</string> - <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) --> - <skip /> - <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) --> - <skip /> - <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) --> - <skip /> - <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) --> - <skip /> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"పరికర ఆటోమేటిక్ సెట్టింగ్"</string> + <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"డిజేబుల్ చేయబడింది"</string> + <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ఎనేబుల్ చేయబడింది"</string> + <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ఈ మార్పును వర్తింపజేయాలంటే మీరు మీ పరికరాన్ని తప్పనిసరిగా రీబూట్ చేయాలి. ఇప్పుడే రీబూట్ చేయండి లేదా రద్దు చేయండి."</string> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"ఆఫీసు <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 0e958e908ac3..a9032ebf4fd2 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ปิดใช้"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"เปิดใช้"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"คุณต้องรีบูตอุปกรณ์เพื่อให้การเปลี่ยนแปลงนี้มีผล รีบูตเลยหรือยกเลิก"</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> ในโปรไฟล์งาน"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 5ce89ebc4c25..712c06bbea74 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -433,7 +433,7 @@ <string name="power_discharge_by" msgid="4113180890060388350">"Tatagal dapat nang hanggang humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_discharge_by_only" msgid="92545648425937000">"Tatagal hanggang mga <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Hanggang <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Posibleng maubos ang baterya sa loob ng <xliff:g id="TIME">%1$s</xliff:g>"</string> + <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Baka maubos ang baterya sa loob ng <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira"</string> <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Mahigit <xliff:g id="TIME_REMAINING">%1$s</xliff:g> pa ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Naka-disable"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Na-enable"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Dapat i-reboot ang iyong device para mailapat ang pagbabagong ito. Mag-reboot ngayon o kanselahin."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> sa Trabaho"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 1e887d19f8ee..8818498bbcfd 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Devre dışı"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Etkin"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu değişikliğin geçerli olması için cihazının yeniden başlatılması gerekir. Şimdi yeniden başlatın veya iptal edin."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> (İş)"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index a95105b72fb1..f6568e3dbb5e 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -226,12 +226,12 @@ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Код підключення Wi‑Fi"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Помилка підключення"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Переконайтеся, що пристрій підключено до тієї ж мережі."</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Підключати пристрій через Wi‑Fi за допомогою QR-коду"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Підключіть пристрій через Wi‑Fi за допомогою QR-коду"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Підключення пристрою…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Не вдалося підключитися до пристрою. Надано неправильний QR-код або пристрій не підключено до тієї ж мережі."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP-адреса та порт"</string> - <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Сканувати QR-код"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Підключати пристрій через Wi‑Fi за допомогою QR-коду"</string> + <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Відскануйте QR-код"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Підключіть пристрій через Wi‑Fi за допомогою QR-коду"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Підключіть пристрій до мережі Wi-Fi"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, налагодження, розробка"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"Ярлик звіту про помилки"</string> @@ -555,6 +555,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Вимкнено"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Увімкнено"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Щоб застосувати ці зміни, перезапустіть пристрій. Перезапустіть пристрій або скасуйте зміни."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Робочий додаток <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml index e056c1c1ecc1..377650351bc5 100644 --- a/packages/SettingsLib/res/values-ur/arrays.xml +++ b/packages/SettingsLib/res/values-ur/arrays.xml @@ -26,7 +26,7 @@ <item msgid="6050951078202663628">"مربوط ہو رہا ہے…"</item> <item msgid="8356618438494652335">"توثیق ہو رہی ہے…"</item> <item msgid="2837871868181677206">"IP پتہ حاصل کر رہا ہے…"</item> - <item msgid="4613015005934755724">"مربوط ہو گیا"</item> + <item msgid="4613015005934755724">"منسلک"</item> <item msgid="3763530049995655072">"معطل شدہ"</item> <item msgid="7852381437933824454">"منقطع کیا جارہا ہے…"</item> <item msgid="5046795712175415059">"منقطع ہو گیا"</item> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 463c1617b1a6..f13f8fb40847 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -433,7 +433,7 @@ <string name="power_discharge_by" msgid="4113180890060388350">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_discharge_by_only" msgid="92545648425937000">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی"</string> <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> تک"</string> - <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"<xliff:g id="TIME">%1$s</xliff:g> تک بیٹری ختم ہو سکتی ہے"</string> + <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"<xliff:g id="TIME">%1$s</xliff:g> تک بیٹری ختم ہو سکتی ہے"</string> <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"<xliff:g id="THRESHOLD">%1$s</xliff:g> سے کم باقی ہے"</string> <string name="power_remaining_less_than_duration" msgid="318215464914990578">"<xliff:g id="THRESHOLD">%1$s</xliff:g> سے کم باقی ہے (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> سے زیادہ باقی ہے (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -549,14 +549,11 @@ <string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string> <string name="guest_nickname" msgid="6332276931583337261">"مہمان"</string> - <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) --> - <skip /> - <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) --> - <skip /> - <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) --> - <skip /> - <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) --> - <skip /> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"آلہ ڈیفالٹ"</string> + <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غیر فعال"</string> + <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string> + <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"اس تبدیلی کو لاگو کرنے کے ليے آپ کے آلہ کو ریبوٹ کرنا ضروری ہے۔ ابھی ریبوٹ کریں یا منسوخ کریں۔"</string> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"دفتر <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 762a246a4453..7b8f627ac0db 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Yoqilmagan"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Yoniq"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Oʻzgarishlar qurilma oʻchib yonganda bajariladi. Hoziroq oʻchib yoqish yoki bekor qilish."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Ish <xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 12241f152dd5..35cecc1a7b9f 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Đã tắt"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Đã bật"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bạn phải khởi động lại thiết bị để áp dụng sự thay đổi này. Hãy khởi động lại ngay hoặc hủy."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"<xliff:g id="APP_NAME">%s</xliff:g> dành cho công việc"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 0a3ca12b8dd1..c69ea2d4b668 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -419,7 +419,7 @@ <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"红色弱视(红绿不分)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"蓝色弱视(蓝黄不分)"</string> <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"色彩校正"</string> - <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"借助颜色校正功能,您可以调整设备上的颜色显示方式"</string> + <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"借助色彩校正功能,您可以调整设备上的颜色显示方式"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"大约还可使用 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已启用"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"设备必须重新启动才能应用此更改。您可以立即重新启动或取消。"</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"工作资料中的<xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 495cc224c5b6..b0324d9a2f46 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -222,7 +222,7 @@ <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"裝置指紋:<xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string> <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"連線失敗"</string> <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"請確認<xliff:g id="DEVICE_NAME">%1$s</xliff:g> 已連線至相同的網絡。"</string> - <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"與裝置配對"</string> + <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"配對裝置"</string> <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi-Fi 配對碼"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"配對失敗"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"請確認裝置已連線至相同的網絡。"</string> @@ -452,7 +452,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充電"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"正在慢速充電"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"非充電中"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"已插入電源插座,但目前無法充電"</string> + <string name="battery_info_status_not_charging" msgid="8330015078868707899">"已連接電源插頭,但目前無法充電"</string> <string name="battery_info_status_full" msgid="4443168946046847468">"電量已滿"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"已由管理員停用"</string> <string name="disabled" msgid="8017887509554714950">"已停用"</string> @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"您的裝置必須重新開機,才能套用此變更。請立即重新開機或取消。"</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"工作設定檔入面嘅「<xliff:g id="APP_NAME">%s</xliff:g>」"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 94452683deca..0574c7ecf626 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"裝置必須重新啟動才能套用這項變更。請立即重新啟動或取消變更。"</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"工作資料夾中的<xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 86698f4134f0..b60553a052e4 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -553,6 +553,7 @@ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ikhutshaziwe"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Inikwe amandla"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Kufanele idivayisi yakho iqaliswe ukuze lolu shintsho lusebenze. Qalisa manje noma khansela."</string> - <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) --> + <string name="accessibility_work_profile_app_description" msgid="5470883112342119165">"Umsebenzi we-<xliff:g id="APP_NAME">%s</xliff:g>"</string> + <!-- no translation found for media_transfer_wired_usb_device_name (7699141088423210903) --> <skip /> </resources> 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/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 9e8c70fcc000..197e34df22a8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -145,15 +145,16 @@ public class LocalMediaManager implements BluetoothCallback { /** * Connect the MediaDevice to transfer media * @param connectDevice the MediaDevice + * @return {@code true} if successfully call, otherwise return {@code false} */ - public void connectDevice(MediaDevice connectDevice) { + public boolean connectDevice(MediaDevice connectDevice) { MediaDevice device = null; synchronized (mMediaDevicesLock) { device = getMediaDeviceById(mMediaDevices, connectDevice.getId()); } if (device == null) { Log.w(TAG, "connectDevice() connectDevice not in the list!"); - return; + return false; } if (device instanceof BluetoothMediaDevice) { final CachedBluetoothDevice cachedDevice = @@ -162,13 +163,13 @@ public class LocalMediaManager implements BluetoothCallback { mOnTransferBluetoothDevice = connectDevice; device.setState(MediaDeviceState.STATE_CONNECTING); cachedDevice.connect(); - return; + return true; } } if (device == mCurrentConnectedDevice) { Log.d(TAG, "connectDevice() this device all ready connected! : " + device.getName()); - return; + return false; } if (mCurrentConnectedDevice != null) { @@ -181,6 +182,7 @@ public class LocalMediaManager implements BluetoothCallback { } else { device.connect(); } + return true; } void dispatchSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state) { 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/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index 365a16c50b99..517071b5511f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -141,7 +141,7 @@ public class LocalMediaManagerTest { when(currentDevice.getId()).thenReturn(TEST_CURRENT_DEVICE_ID); mLocalMediaManager.registerCallback(mCallback); - mLocalMediaManager.connectDevice(device); + assertThat(mLocalMediaManager.connectDevice(device)).isTrue(); verify(currentDevice).disconnect(); verify(device).connect(); @@ -154,7 +154,7 @@ public class LocalMediaManagerTest { mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1; mLocalMediaManager.registerCallback(mCallback); - mLocalMediaManager.connectDevice(mInfoMediaDevice2); + assertThat(mLocalMediaManager.connectDevice(mInfoMediaDevice2)).isTrue(); assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState .STATE_CONNECTING); @@ -167,7 +167,7 @@ public class LocalMediaManagerTest { mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1; mLocalMediaManager.registerCallback(mCallback); - mLocalMediaManager.connectDevice(mInfoMediaDevice1); + assertThat(mLocalMediaManager.connectDevice(mInfoMediaDevice1)).isFalse(); assertThat(mInfoMediaDevice1.getState()).isNotEqualTo(LocalMediaManager.MediaDeviceState .STATE_CONNECTING); @@ -185,7 +185,7 @@ public class LocalMediaManagerTest { when(cachedDevice.isBusy()).thenReturn(false); mLocalMediaManager.registerCallback(mCallback); - mLocalMediaManager.connectDevice(device); + assertThat(mLocalMediaManager.connectDevice(device)).isTrue(); verify(cachedDevice).connect(); } 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/drawable/stat_sys_media.xml b/packages/SystemUI/res/drawable/stat_sys_media.xml new file mode 100644 index 000000000000..d48db7bd0d28 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_media.xml @@ -0,0 +1,31 @@ +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M5,7.81l0,8.38l6,-4.19z" + android:fillColor="#000000"/> + <path + android:pathData="M13,8h2v8h-2z" + android:fillColor="#000000"/> + <path + android:pathData="M17,8h2v8h-2z" + android:fillColor="#000000"/> +</vector> 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/strings.xml b/packages/SystemUI/res/values/strings.xml index 8bbcfa0e7898..de483179a92a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -790,6 +790,9 @@ <!-- Accessibility text describing sensors off active. [CHAR LIMIT=NONE] --> <string name="accessibility_sensors_off_active">Sensors off active</string> + <!-- Accessibility text describing that media is playing. [CHAR LIMIT=NONE] --> + <string name="accessibility_media_active">Media is active</string> + <!-- Content description of the clear button in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_clear_all">Clear all notifications.</string> 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/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index a1f4c9666423..181170b0dee5 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -89,35 +89,36 @@ class ControlsControllerImpl @Inject constructor ( contentResolver, CONTROLS_AVAILABLE, DEFAULT_ENABLED, currentUserId) != 0 private set - private var file = Environment.buildPath( - context.filesDir, - ControlsFavoritePersistenceWrapper.FILE_NAME - ) - private var auxiliaryFile = Environment.buildPath( - context.filesDir, - AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME - ) - private val persistenceWrapper = optionalWrapper.orElseGet { - ControlsFavoritePersistenceWrapper( - file, - executor, - BackupManager(context) + private val persistenceWrapper: ControlsFavoritePersistenceWrapper + @VisibleForTesting + internal var auxiliaryPersistenceWrapper: AuxiliaryPersistenceWrapper + + init { + val userStructure = UserStructure(context, currentUser) + + persistenceWrapper = optionalWrapper.orElseGet { + ControlsFavoritePersistenceWrapper( + userStructure.file, + executor, + BackupManager(userStructure.userContext) + ) + } + + auxiliaryPersistenceWrapper = AuxiliaryPersistenceWrapper( + userStructure.auxiliaryFile, + executor ) } - @VisibleForTesting - internal var auxiliaryPersistenceWrapper = AuxiliaryPersistenceWrapper(auxiliaryFile, executor) - private fun setValuesForUser(newUser: UserHandle) { Log.d(TAG, "Changing to user: $newUser") currentUser = newUser - val userContext = context.createContextAsUser(currentUser, 0) - file = Environment.buildPath( - userContext.filesDir, ControlsFavoritePersistenceWrapper.FILE_NAME) - auxiliaryFile = Environment.buildPath( - userContext.filesDir, AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME) - persistenceWrapper.changeFileAndBackupManager(file, BackupManager(userContext)) - auxiliaryPersistenceWrapper.changeFile(auxiliaryFile) + val userStructure = UserStructure(context, currentUser) + persistenceWrapper.changeFileAndBackupManager( + userStructure.file, + BackupManager(userStructure.userContext) + ) + auxiliaryPersistenceWrapper.changeFile(userStructure.auxiliaryFile) available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE, DEFAULT_ENABLED, newUser.identifier) != 0 resetFavorites(available) @@ -564,6 +565,20 @@ class ControlsControllerImpl @Inject constructor ( } } +class UserStructure(context: Context, user: UserHandle) { + val userContext = context.createContextAsUser(user, 0) + + val file = Environment.buildPath( + context.filesDir, + ControlsFavoritePersistenceWrapper.FILE_NAME + ) + + val auxiliaryFile = Environment.buildPath( + context.filesDir, + AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME + ) +} + /** * Relies on immutable data for thread safety. When necessary to update favMap, use reassignment to * replace it, which will not disrupt any ongoing map traversal. 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/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index 17e423416064..f979bbb7af35 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -18,10 +18,15 @@ package com.android.systemui.controls.ui import android.animation.Animator import android.animation.AnimatorListenerAdapter +import android.animation.AnimatorSet +import android.animation.ObjectAnimator import android.animation.ValueAnimator +import android.annotation.ColorRes import android.app.Dialog import android.content.Context +import android.content.res.ColorStateList import android.graphics.drawable.ClipDrawable +import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.service.controls.Control @@ -34,6 +39,7 @@ import android.service.controls.templates.TemperatureControlTemplate import android.service.controls.templates.ToggleRangeTemplate import android.service.controls.templates.ToggleTemplate import android.util.MathUtils +import android.util.TypedValue import android.view.View import android.view.ViewGroup import android.widget.ImageView @@ -63,6 +69,8 @@ class ControlViewHolder( private const val UPDATE_DELAY_IN_MILLIS = 3000L private const val ALPHA_ENABLED = 255 private const val ALPHA_DISABLED = 0 + private const val STATUS_ALPHA_ENABLED = 1f + private const val STATUS_ALPHA_DIMMED = 0.45f private val FORCE_PANEL_DEVICES = setOf( DeviceTypes.TYPE_THERMOSTAT, DeviceTypes.TYPE_CAMERA @@ -94,9 +102,11 @@ class ControlViewHolder( private val toggleBackgroundIntensity: Float = layout.context.resources .getFraction(R.fraction.controls_toggle_bg_intensity, 1, 1) private var stateAnimator: ValueAnimator? = null + private var statusAnimator: Animator? = null private val baseLayer: GradientDrawable val icon: ImageView = layout.requireViewById(R.id.icon) - val status: TextView = layout.requireViewById(R.id.status) + private val status: TextView = layout.requireViewById(R.id.status) + private var nextStatusText: CharSequence = "" val title: TextView = layout.requireViewById(R.id.title) val subtitle: TextView = layout.requireViewById(R.id.subtitle) val context: Context = layout.getContext() @@ -105,6 +115,7 @@ class ControlViewHolder( var cancelUpdate: Runnable? = null var behavior: Behavior? = null var lastAction: ControlAction? = null + var isLoading = false private var lastChallengeDialog: Dialog? = null private val onDialogCancel: () -> Unit = { lastChallengeDialog = null } @@ -144,6 +155,7 @@ class ControlViewHolder( }) } + isLoading = false behavior = bindBehavior(behavior, findBehaviorClass(controlStatus, template, deviceType)) updateContentDescription() } @@ -189,11 +201,11 @@ class ControlViewHolder( val previousText = status.getText() cancelUpdate = uiExecutor.executeDelayed({ - status.setText(previousText) - updateContentDescription() - }, UPDATE_DELAY_IN_MILLIS) + setStatusText(previousText) + updateContentDescription() + }, UPDATE_DELAY_IN_MILLIS) - status.setText(tempStatus) + setStatusText(tempStatus) updateContentDescription() } @@ -231,18 +243,50 @@ class ControlViewHolder( } internal fun applyRenderInfo(enabled: Boolean, offset: Int, animated: Boolean = true) { - setEnabled(enabled) - val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset) - val fg = context.resources.getColorStateList(ri.foreground, context.theme) + val newText = nextStatusText + nextStatusText = "" + val control = cws.control + + var shouldAnimate = animated + if (newText == status.text) { + shouldAnimate = false + } + animateStatusChange(shouldAnimate) { + updateStatusRow(enabled, newText, ri.icon, fg, control) + } + + animateBackgroundChange(shouldAnimate, enabled, ri.enabledBackground) + } + + fun getStatusText() = status.text + + fun setStatusTextSize(textSize: Float) = + status.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) + + fun setStatusText(text: CharSequence, immediately: Boolean = false) { + if (immediately) { + status.alpha = STATUS_ALPHA_ENABLED + status.text = text + nextStatusText = "" + } else { + nextStatusText = text + } + } + + private fun animateBackgroundChange( + animated: Boolean, + enabled: Boolean, + @ColorRes bgColor: Int + ) { val bg = context.resources.getColor(R.color.control_default_background, context.theme) var (newClipColor, newAlpha) = if (enabled) { // allow color overrides for the enabled state only val color = cws.control?.getCustomColor()?.let { val state = intArrayOf(android.R.attr.state_enabled) it.getColorForState(state, it.getDefaultColor()) - } ?: context.resources.getColor(ri.enabledBackground, context.theme) + } ?: context.resources.getColor(bgColor, context.theme) listOf(color, ALPHA_ENABLED) } else { listOf( @@ -251,21 +295,6 @@ class ControlViewHolder( ) } - status.setTextColor(fg) - - cws.control?.getCustomIcon()?.let { - // do not tint custom icons, assume the intended icon color is correct - icon.imageTintList = null - icon.setImageIcon(it) - } ?: run { - icon.setImageDrawable(ri.icon) - - // do not color app icons - if (deviceType != DeviceTypes.TYPE_ROUTINE) { - icon.imageTintList = fg - } - } - (clipLayer.getDrawable() as GradientDrawable).apply { val newBaseColor = if (behavior is ToggleRangeBehavior) { ColorUtils.blendARGB(bg, newClipColor, toggleBackgroundIntensity) @@ -303,6 +332,77 @@ class ControlViewHolder( } } + private fun animateStatusChange(animated: Boolean, statusRowUpdater: () -> Unit) { + statusAnimator?.cancel() + + if (!animated) { + statusRowUpdater.invoke() + return + } + + if (isLoading) { + statusRowUpdater.invoke() + statusAnimator = ObjectAnimator.ofFloat(status, "alpha", STATUS_ALPHA_DIMMED).apply { + repeatMode = ValueAnimator.REVERSE + repeatCount = ValueAnimator.INFINITE + duration = 500L + interpolator = Interpolators.LINEAR + startDelay = 900L + start() + } + } else { + val fadeOut = ObjectAnimator.ofFloat(status, "alpha", 0f).apply { + duration = 200L + interpolator = Interpolators.LINEAR + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator?) { + statusRowUpdater.invoke() + } + }) + } + val fadeIn = ObjectAnimator.ofFloat(status, "alpha", STATUS_ALPHA_ENABLED).apply { + duration = 200L + interpolator = Interpolators.LINEAR + } + statusAnimator = AnimatorSet().apply { + playSequentially(fadeOut, fadeIn) + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator?) { + status.alpha = STATUS_ALPHA_ENABLED + statusAnimator = null + } + }) + start() + } + } + } + + private fun updateStatusRow( + enabled: Boolean, + text: CharSequence, + drawable: Drawable, + color: ColorStateList, + control: Control? + ) { + setEnabled(enabled) + + status.text = text + status.setTextColor(color) + + control?.getCustomIcon()?.let { + // do not tint custom icons, assume the intended icon color is correct + icon.imageTintList = null + icon.setImageIcon(it) + } ?: run { + icon.setImageDrawable(drawable) + + // do not color app icons + if (deviceType != DeviceTypes.TYPE_ROUTINE) { + icon.imageTintList = color + } + } + } + private fun setEnabled(enabled: Boolean) { status.setEnabled(enabled) icon.setEnabled(enabled) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 52d564da7890..3aa417ab904b 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -59,6 +59,7 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.globalactions.GlobalActionsPopupMenu import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.statusbar.phone.ShadeController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.DelayableExecutor import dagger.Lazy @@ -79,7 +80,8 @@ class ControlsUiControllerImpl @Inject constructor ( @Main val sharedPreferences: SharedPreferences, val controlActionCoordinator: ControlActionCoordinator, private val activityStarter: ActivityStarter, - private val keyguardStateController: KeyguardStateController + private val keyguardStateController: KeyguardStateController, + private val shadeController: ShadeController ) : ControlsUiController { companion object { @@ -254,14 +256,11 @@ class ControlsUiControllerImpl @Inject constructor ( intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true) dismissGlobalActions.run() - if (!keyguardStateController.isUnlocked()) { - activityStarter.dismissKeyguardThenExecute({ - context.startActivity(intent) - true - }, null, true) - } else { + activityStarter.dismissKeyguardThenExecute({ + shadeController.collapsePanel(false) context.startActivity(intent) - } + true + }, null, true) } private fun showControlsView(items: List<SelectionItem>) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt index 722ade923208..0c8e3ffc5201 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt @@ -24,7 +24,7 @@ class DefaultBehavior : Behavior { } override fun bind(cws: ControlWithState, colorOffset: Int) { - cvh.status.setText(cws.control?.getStatusText() ?: "") + cvh.setStatusText(cws.control?.getStatusText() ?: "") cvh.applyRenderInfo(false, colorOffset) } } 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/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt index 124df32af5e0..ba331f459a0d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt @@ -97,6 +97,8 @@ private const val BUCKET_SIZE = 1000 private const val THERMOSTAT_RANGE = DeviceTypes.TYPE_THERMOSTAT * BUCKET_SIZE private val deviceColorMap = mapOf<Int, Pair<Int, Int>>( + (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_OFF) to + Pair(R.color.control_default_foreground, R.color.control_default_background), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to Pair(R.color.thermo_heat_foreground, R.color.control_enabled_thermo_heat_background), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to @@ -108,13 +110,9 @@ private val deviceColorMap = mapOf<Int, Pair<Int, Int>>( } private val deviceIconMap = mapOf<Int, IconState>( - THERMOSTAT_RANGE to IconState( - R.drawable.ic_device_thermostat_off, - R.drawable.ic_device_thermostat_on - ), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_OFF) to IconState( R.drawable.ic_device_thermostat_off, - R.drawable.ic_device_thermostat_on + R.drawable.ic_device_thermostat_off ), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to IconState( R.drawable.ic_device_thermostat_off, @@ -130,7 +128,7 @@ private val deviceIconMap = mapOf<Int, IconState>( ), (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_ECO) to IconState( R.drawable.ic_device_thermostat_off, - R.drawable.ic_device_thermostat_on + R.drawable.ic_device_thermostat_off ), DeviceTypes.TYPE_THERMOSTAT to IconState( R.drawable.ic_device_thermostat_off, diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt index d8dceba35251..bf3835dba4fd 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt @@ -32,9 +32,12 @@ class StatusBehavior : Behavior { val msg = when (status) { Control.STATUS_ERROR -> R.string.controls_error_generic Control.STATUS_NOT_FOUND -> R.string.controls_error_removed - else -> com.android.internal.R.string.loading + else -> { + cvh.isLoading = true + com.android.internal.R.string.loading + } } - cvh.status.setText(cvh.context.getString(msg)) + cvh.setStatusText(cvh.context.getString(msg)) cvh.applyRenderInfo(false, colorOffset) } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt index 2795c7af6c82..a7dc09bb17e5 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt @@ -39,7 +39,7 @@ class TemperatureControlBehavior : Behavior { override fun bind(cws: ControlWithState, colorOffset: Int) { this.control = cws.control!! - cvh.status.setText(control.getStatusText()) + cvh.setStatusText(control.getStatusText()) val ld = cvh.layout.getBackground() as LayerDrawable clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt index c432c09eeda4..dc7247cd7096 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt @@ -34,7 +34,6 @@ class ToggleBehavior : Behavior { override fun initialize(cvh: ControlViewHolder) { this.cvh = cvh - cvh.applyRenderInfo(false /* enabled */, 0 /* offset */, false /* animated */) cvh.layout.setOnClickListener(View.OnClickListener() { cvh.controlActionCoordinator.toggle(cvh, template.getTemplateId(), template.isChecked()) @@ -44,7 +43,7 @@ class ToggleBehavior : Behavior { override fun bind(cws: ControlWithState, colorOffset: Int) { this.control = cws.control!! - cvh.status.setText(control.getStatusText()) + cvh.setStatusText(control.getStatusText()) val controlTemplate = control.getControlTemplate() template = when (controlTemplate) { is ToggleTemplate -> controlTemplate diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt index a09ed09bfcec..1f0ca9be2ecc 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt @@ -31,7 +31,6 @@ import android.service.controls.templates.TemperatureControlTemplate import android.service.controls.templates.ToggleRangeTemplate import android.util.Log import android.util.MathUtils -import android.util.TypedValue import android.view.GestureDetector import android.view.GestureDetector.SimpleOnGestureListener import android.view.MotionEvent @@ -39,7 +38,6 @@ import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo -import android.widget.TextView import com.android.systemui.Interpolators import com.android.systemui.R import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL @@ -57,7 +55,6 @@ class ToggleRangeBehavior : Behavior { lateinit var control: Control lateinit var cvh: ControlViewHolder lateinit var rangeTemplate: RangeTemplate - lateinit var status: TextView lateinit var context: Context var currentStatusText: CharSequence = "" var currentRangeValue: String = "" @@ -71,10 +68,7 @@ class ToggleRangeBehavior : Behavior { override fun initialize(cvh: ControlViewHolder) { this.cvh = cvh - status = cvh.status - context = status.getContext() - - cvh.applyRenderInfo(false /* enabled */, colorOffset, false /* animated */) + context = cvh.context val gestureListener = ToggleRangeGestureListener(cvh.layout) val gestureDetector = GestureDetector(context, gestureListener) @@ -131,7 +125,6 @@ class ToggleRangeBehavior : Behavior { this.colorOffset = colorOffset currentStatusText = control.getStatusText() - status.setText(currentStatusText) // ControlViewHolder sets a long click listener, but we want to handle touch in // here instead, otherwise we'll have state conflicts. @@ -222,7 +215,7 @@ class ToggleRangeBehavior : Behavior { } fun beginUpdateRange() { - status.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources() + cvh.setStatusTextSize(context.getResources() .getDimensionPixelSize(R.dimen.control_status_expanded).toFloat()) } @@ -261,14 +254,13 @@ class ToggleRangeBehavior : Behavior { val newValue = levelToRangeValue(newLevel) currentRangeValue = format(rangeTemplate.getFormatString().toString(), DEFAULT_FORMAT, newValue) - val text = if (isDragging) { - currentRangeValue + if (isDragging) { + cvh.setStatusText(currentRangeValue, /* immediately */ true) } else { - "$currentStatusText $currentRangeValue" + cvh.setStatusText("$currentStatusText $currentRangeValue") } - status.setText(text) } else { - status.setText(currentStatusText) + cvh.setStatusText(currentStatusText) } } @@ -296,9 +288,9 @@ class ToggleRangeBehavior : Behavior { } fun endUpdateRange() { - status.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources() + cvh.setStatusTextSize(context.getResources() .getDimensionPixelSize(R.dimen.control_status_normal).toFloat()) - status.setText("$currentStatusText $currentRangeValue") + cvh.setStatusText("$currentStatusText $currentRangeValue", /* immediately */ true) cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(levelToRangeValue(clipLayer.getLevel())))) } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt index 8ce2e617669c..48f945874135 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt @@ -23,6 +23,7 @@ import android.service.controls.Control import android.service.controls.templates.ControlTemplate import com.android.systemui.R +import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL /** @@ -37,7 +38,6 @@ class TouchBehavior : Behavior { override fun initialize(cvh: ControlViewHolder) { this.cvh = cvh - cvh.applyRenderInfo(false /* enabled */, 0 /* offset */, false /* animated */) cvh.layout.setOnClickListener(View.OnClickListener() { cvh.controlActionCoordinator.touch(cvh, template.getTemplateId(), control) @@ -46,13 +46,14 @@ class TouchBehavior : Behavior { override fun bind(cws: ControlWithState, colorOffset: Int) { this.control = cws.control!! - cvh.status.setText(control.getStatusText()) + cvh.setStatusText(control.getStatusText()) template = control.getControlTemplate() val ld = cvh.layout.getBackground() as LayerDrawable clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) - clipLayer.setLevel(MIN_LEVEL) - cvh.applyRenderInfo(false, colorOffset) + val enabled = if (colorOffset > 0) true else false + clipLayer.setLevel(if (enabled) MAX_LEVEL else MIN_LEVEL) + cvh.applyRenderInfo(enabled, colorOffset) } } 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 8e1854a469f1..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) -> { @@ -2221,7 +2231,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mBackgroundDrawable.setAlpha(0); float xOffset = mGlobalActionsLayout.getAnimationOffsetX(); ObjectAnimator alphaAnimator = - ObjectAnimator.ofFloat(mContainer, "transitionAlpha", 0f, 1f); + ObjectAnimator.ofFloat(mContainer, "alpha", 0f, 1f); alphaAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); alphaAnimator.setDuration(183); alphaAnimator.addUpdateListener((animation) -> { @@ -2234,8 +2244,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, ObjectAnimator xAnimator = ObjectAnimator.ofFloat(mContainer, "translationX", xOffset, 0f); - alphaAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); - alphaAnimator.setDuration(350); + xAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + xAnimator.setDuration(350); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(alphaAnimator, xAnimator); @@ -2247,7 +2257,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, dismissWithAnimation(() -> { mContainer.setTranslationX(0); ObjectAnimator alphaAnimator = - ObjectAnimator.ofFloat(mContainer, "transitionAlpha", 1f, 0f); + ObjectAnimator.ofFloat(mContainer, "alpha", 1f, 0f); alphaAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); alphaAnimator.setDuration(233); alphaAnimator.addUpdateListener((animation) -> { @@ -2261,8 +2271,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, float xOffset = mGlobalActionsLayout.getAnimationOffsetX(); ObjectAnimator xAnimator = ObjectAnimator.ofFloat(mContainer, "translationX", 0f, xOffset); - alphaAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); - alphaAnimator.setDuration(350); + xAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); + xAnimator.setDuration(350); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(alphaAnimator, xAnimator); @@ -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..85e1c6b77be4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt @@ -18,6 +18,7 @@ package com.android.systemui.media import android.view.View import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.stack.MediaHeaderView @@ -33,7 +34,8 @@ import javax.inject.Singleton class KeyguardMediaController @Inject constructor( private val mediaHost: MediaHost, private val bypassController: KeyguardBypassController, - private val statusBarStateController: SysuiStatusBarStateController + private val statusBarStateController: SysuiStatusBarStateController, + private val notifLockscreenUserManager: NotificationLockscreenUserManager ) { init { @@ -61,11 +63,12 @@ class KeyguardMediaController @Inject constructor( } private fun updateVisibility() { - val shouldBeVisible = mediaHost.visible - && !bypassController.bypassEnabled - && (statusBarStateController.state == StatusBarState.KEYGUARD || - statusBarStateController.state == StatusBarState.FULLSCREEN_USER_SWITCHER) + val keyguardOrUserSwitcher = (statusBarStateController.state == StatusBarState.KEYGUARD || + statusBarStateController.state == StatusBarState.FULLSCREEN_USER_SWITCHER) + val shouldBeVisible = mediaHost.visible && + !bypassController.bypassEnabled && + keyguardOrUserSwitcher && + notifLockscreenUserManager.shouldShowLockscreenNotifications() 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/LocalMediaManagerFactory.kt b/packages/SystemUI/src/com/android/systemui/media/LocalMediaManagerFactory.kt new file mode 100644 index 000000000000..94a0835f22f8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/LocalMediaManagerFactory.kt @@ -0,0 +1,40 @@ +/* + * 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.Context + +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.settingslib.media.InfoMediaManager +import com.android.settingslib.media.LocalMediaManager + +import javax.inject.Inject + +/** + * Factory to create [LocalMediaManager] objects. + */ +class LocalMediaManagerFactory @Inject constructor( + private val context: Context, + private val localBluetoothManager: LocalBluetoothManager? +) { + /** Creates a [LocalMediaManager] for the given package. */ + fun create(packageName: String): LocalMediaManager { + return InfoMediaManager(context, packageName, null, localBluetoothManager).run { + LocalMediaManager(context, localBluetoothManager, this, packageName) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index e08d7a5b97a6..f90798bd30b8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -41,7 +41,6 @@ import android.util.Log; import android.view.View; import android.widget.ImageButton; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; @@ -56,14 +55,11 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawable; import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; import com.android.settingslib.Utils; -import com.android.settingslib.media.LocalMediaManager; -import com.android.settingslib.media.MediaDevice; import com.android.settingslib.media.MediaOutputSliceConstants; import com.android.settingslib.widget.AdaptiveIcon; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.QSMediaBrowser; -import com.android.systemui.util.Assert; import com.android.systemui.util.concurrency.DelayableExecutor; import org.jetbrains.annotations.NotNull; @@ -77,7 +73,6 @@ import java.util.concurrent.Executor; */ public class MediaControlPanel { private static final String TAG = "MediaControlPanel"; - @Nullable private final LocalMediaManager mLocalMediaManager; // Button IDs for QS controls static final int[] ACTION_IDS = { @@ -100,7 +95,6 @@ public class MediaControlPanel { private MediaSession.Token mToken; private MediaController mController; private int mBackgroundColor; - private MediaDevice mDevice; protected ComponentName mServiceComponent; private boolean mIsRegistered = false; private List<KeyFrames> mKeyFrames; @@ -113,7 +107,6 @@ public class MediaControlPanel { public static final String MEDIA_PREFERENCE_KEY = "browser_components"; private SharedPreferences mSharedPrefs; private boolean mCheckedForResumption = false; - private boolean mIsRemotePlayback; private QSMediaBrowser mQSMediaBrowser; private final MediaController.Callback mSessionCallback = new MediaController.Callback() { @@ -122,7 +115,6 @@ public class MediaControlPanel { Log.d(TAG, "session destroyed"); mController.unregisterCallback(mSessionCallback); clearControls(); - makeInactive(); } @Override public void onPlaybackStateChanged(PlaybackState state) { @@ -130,31 +122,6 @@ public class MediaControlPanel { if (s == PlaybackState.STATE_NONE) { Log.d(TAG, "playback state change will trigger resumption, state=" + state); clearControls(); - makeInactive(); - } - } - }; - - private final LocalMediaManager.DeviceCallback mDeviceCallback = - new LocalMediaManager.DeviceCallback() { - @Override - public void onDeviceListUpdate(List<MediaDevice> devices) { - if (mLocalMediaManager == null) { - return; - } - MediaDevice currentDevice = mLocalMediaManager.getCurrentConnectedDevice(); - // Check because this can be called several times while changing devices - if (mDevice == null || !mDevice.equals(currentDevice)) { - mDevice = currentDevice; - updateDevice(mDevice); - } - } - - @Override - public void onSelectedDeviceStateChanged(MediaDevice device, int state) { - if (mDevice == null || !mDevice.equals(device)) { - mDevice = device; - updateDevice(mDevice); } } }; @@ -162,16 +129,13 @@ public class MediaControlPanel { /** * Initialize a new control panel * @param context - * @param routeManager Manager used to listen for device change events. * @param foregroundExecutor foreground executor * @param backgroundExecutor background executor, used for processing artwork * @param activityStarter activity starter */ - public MediaControlPanel(Context context, @Nullable LocalMediaManager routeManager, - Executor foregroundExecutor, DelayableExecutor backgroundExecutor, - ActivityStarter activityStarter) { + public MediaControlPanel(Context context, Executor foregroundExecutor, + DelayableExecutor backgroundExecutor, ActivityStarter activityStarter) { mContext = context; - mLocalMediaManager = routeManager; mForegroundExecutor = foregroundExecutor; mBackgroundExecutor = backgroundExecutor; mActivityStarter = activityStarter; @@ -183,7 +147,6 @@ public class MediaControlPanel { if (mSeekBarObserver != null) { mSeekBarViewModel.getProgress().removeObserver(mSeekBarObserver); } - makeInactive(); } private void loadDimens() { @@ -228,7 +191,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()); @@ -318,34 +281,67 @@ public class MediaControlPanel { artistText.setText(data.getArtist()); // Transfer chip - if (mLocalMediaManager != null) { - mViewHolder.getSeamless().setVisibility(View.VISIBLE); - setVisibleAndAlpha(collapsedSet, R.id.media_seamless, true /*visible */); - setVisibleAndAlpha(expandedSet, R.id.media_seamless, true /*visible */); - updateDevice(mLocalMediaManager.getCurrentConnectedDevice()); - if (mViewHolder.getBackground().getBackground() instanceof IlluminationDrawable) { - ((IlluminationDrawable) mViewHolder.getBackground().getBackground()) - .setupTouch(mViewHolder.getSeamless(), mViewHolder.getPlayer()); - } - mViewHolder.getSeamless().setOnClickListener(v -> { - final Intent intent = new Intent() - .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT) - .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, - mController.getPackageName()) - .putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, mToken); - mActivityStarter.startActivity(intent, false, true /* dismissShade */, - Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - }); - } else { - Log.d(TAG, "LocalMediaManager is null. Not binding output chip for pkg=" + pkgName); - } + mViewHolder.getSeamless().setVisibility(View.VISIBLE); + setVisibleAndAlpha(collapsedSet, R.id.media_seamless, true /*visible */); + setVisibleAndAlpha(expandedSet, R.id.media_seamless, true /*visible */); + mViewHolder.getSeamless().setOnClickListener(v -> { + final Intent intent = new Intent() + .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT) + .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, + mController.getPackageName()) + .putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, mToken); + mActivityStarter.startActivity(intent, false, true /* dismissShade */, + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + }); + final boolean isRemotePlayback; PlaybackInfo playbackInfo = mController.getPlaybackInfo(); if (playbackInfo != null) { - mIsRemotePlayback = playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE; + isRemotePlayback = playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE; } else { Log.d(TAG, "PlaybackInfo was null. Defaulting to local playback."); - mIsRemotePlayback = false; + isRemotePlayback = false; + } + + ImageView iconView = mViewHolder.getSeamlessIcon(); + TextView deviceName = mViewHolder.getSeamlessText(); + + // Update the outline color + RippleDrawable bkgDrawable = (RippleDrawable) mViewHolder.getSeamless().getBackground(); + GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0); + rect.setStroke(2, deviceName.getCurrentTextColor()); + rect.setColor(Color.TRANSPARENT); + + if (isRemotePlayback) { + mViewHolder.getSeamless().setEnabled(false); + // TODO(b/156875717): setEnabled should cause the alpha to change. + mViewHolder.getSeamless().setAlpha(0.38f); + iconView.setImageResource(R.drawable.ic_hardware_speaker); + iconView.setVisibility(View.VISIBLE); + deviceName.setText(R.string.media_seamless_remote_device); + } else if (data.getDevice() != null && data.getDevice().getIcon() != null + && data.getDevice().getName() != null) { + mViewHolder.getSeamless().setEnabled(true); + mViewHolder.getSeamless().setAlpha(1f); + Drawable icon = data.getDevice().getIcon(); + iconView.setVisibility(View.VISIBLE); + + if (icon instanceof AdaptiveIcon) { + AdaptiveIcon aIcon = (AdaptiveIcon) icon; + aIcon.setBackgroundColor(mBackgroundColor); + iconView.setImageDrawable(aIcon); + } else { + iconView.setImageDrawable(icon); + } + deviceName.setText(data.getDevice().getName()); + } else { + // Reset to default + Log.w(TAG, "device is null. Not binding output chip."); + mViewHolder.getSeamless().setEnabled(true); + mViewHolder.getSeamless().setAlpha(1f); + iconView.setVisibility(View.GONE); + deviceName.setText(com.android.internal.R.string.ext_media_seamless_action); } + List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact(); // Media controls int i = 0; @@ -358,11 +354,6 @@ public class MediaControlPanel { button.setContentDescription(mediaAction.getContentDescription()); PendingIntent actionIntent = mediaAction.getIntent(); - if (mViewHolder.getBackground().getBackground() instanceof IlluminationDrawable) { - ((IlluminationDrawable) mViewHolder.getBackground().getBackground()) - .setupTouch(button, mViewHolder.getPlayer()); - } - button.setOnClickListener(v -> { if (actionIntent != null) { try { @@ -391,8 +382,6 @@ public class MediaControlPanel { // Set up long press menu // TODO: b/156036025 bring back media guts - makeActive(); - // Update both constraint sets to regenerate the animation. mViewHolder.getPlayer().updateState(R.id.collapsed, collapsedSet); mViewHolder.getPlayer().updateState(R.id.expanded, expandedSet); @@ -524,60 +513,6 @@ public class MediaControlPanel { } /** - * Update the current device information - * @param device device information to display - */ - private void updateDevice(MediaDevice device) { - mForegroundExecutor.execute(() -> { - updateChipInternal(device); - }); - } - - private void updateChipInternal(MediaDevice device) { - if (mViewHolder == null) { - return; - } - ImageView iconView = mViewHolder.getSeamlessIcon(); - TextView deviceName = mViewHolder.getSeamlessText(); - - // Update the outline color - LinearLayout viewLayout = (LinearLayout) mViewHolder.getSeamless(); - RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground(); - GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0); - rect.setStroke(2, deviceName.getCurrentTextColor()); - rect.setColor(Color.TRANSPARENT); - - if (mIsRemotePlayback) { - mViewHolder.getSeamless().setEnabled(false); - mViewHolder.getSeamless().setAlpha(0.38f); - iconView.setImageResource(R.drawable.ic_hardware_speaker); - iconView.setVisibility(View.VISIBLE); - deviceName.setText(R.string.media_seamless_remote_device); - } else if (device != null) { - mViewHolder.getSeamless().setEnabled(true); - mViewHolder.getSeamless().setAlpha(1f); - Drawable icon = device.getIcon(); - iconView.setVisibility(View.VISIBLE); - - if (icon instanceof AdaptiveIcon) { - AdaptiveIcon aIcon = (AdaptiveIcon) icon; - aIcon.setBackgroundColor(mBackgroundColor); - iconView.setImageDrawable(aIcon); - } else { - iconView.setImageDrawable(icon); - } - deviceName.setText(device.getName()); - } else { - // Reset to default - Log.d(TAG, "device is null. Not binding output chip."); - mViewHolder.getSeamless().setEnabled(true); - mViewHolder.getSeamless().setAlpha(1f); - iconView.setVisibility(View.GONE); - deviceName.setText(com.android.internal.R.string.ext_media_seamless_action); - } - } - - /** * Puts controls into a resumption state if possible, or calls removePlayer if no component was * found that could resume playback */ @@ -651,27 +586,6 @@ public class MediaControlPanel { set.setAlpha(actionId, visible ? 1.0f : 0.0f); } - private void makeActive() { - Assert.isMainThread(); - if (!mIsRegistered) { - if (mLocalMediaManager != null) { - mLocalMediaManager.registerCallback(mDeviceCallback); - mLocalMediaManager.startScan(); - } - mIsRegistered = true; - } - } - - private void makeInactive() { - Assert.isMainThread(); - if (mIsRegistered) { - if (mLocalMediaManager != null) { - mLocalMediaManager.stopScan(); - mLocalMediaManager.unregisterCallback(mDeviceCallback); - } - mIsRegistered = false; - } - } /** * Verify that we can connect to the given component with a MediaBrowser, and if so, add that * component to the list of resumption components diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt index 85965d03a096..41d411019921 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt @@ -34,7 +34,8 @@ data class MediaData( val actionsToShowInCompact: List<Int>, val packageName: String?, val token: MediaSession.Token?, - val clickIntent: PendingIntent? + val clickIntent: PendingIntent?, + val device: MediaDeviceData? ) /** State of a media action. */ @@ -43,3 +44,9 @@ data class MediaAction( val intent: PendingIntent?, val contentDescription: CharSequence? ) + +/** State of the media device. */ +data class MediaDeviceData( + val icon: Drawable?, + val name: String? +) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt new file mode 100644 index 000000000000..cce9838bb8e2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media + +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Combines updates from [MediaDataManager] with [MediaDeviceManager]. + */ +@Singleton +class MediaDataCombineLatest @Inject constructor( + private val dataSource: MediaDataManager, + private val deviceSource: MediaDeviceManager +) { + private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() + private val entries: MutableMap<String, Pair<MediaData?, MediaDeviceData?>> = mutableMapOf() + + init { + dataSource.addListener(object : MediaDataManager.Listener { + override fun onMediaDataLoaded(key: String, data: MediaData) { + entries[key] = data to entries[key]?.second + update(key) + } + override fun onMediaDataRemoved(key: String) { + remove(key) + } + }) + deviceSource.addListener(object : MediaDeviceManager.Listener { + override fun onMediaDeviceChanged(key: String, data: MediaDeviceData?) { + entries[key] = entries[key]?.first to data + update(key) + } + override fun onKeyRemoved(key: String) { + remove(key) + } + }) + } + + /** + * Add a listener for [MediaData] changes that has been combined with latest [MediaDeviceData]. + */ + fun addListener(listener: MediaDataManager.Listener) = listeners.add(listener) + + /** + * Remove a listener registered with addListener. + */ + fun removeListener(listener: MediaDataManager.Listener) = listeners.remove(listener) + + private fun update(key: String) { + val (entry, device) = entries[key] ?: null to null + if (entry != null && device != null) { + val data = entry.copy(device = device) + listeners.forEach { + it.onMediaDataLoaded(key, data) + } + } + } + + private fun remove(key: String) { + entries.remove(key)?.let { + listeners.forEach { + it.onMediaDataRemoved(key) + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 90c558a1ee97..8cbe3ecf5387 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -55,7 +55,19 @@ private const val LUMINOSITY_THRESHOLD = 0.05f private const val SATURATION_MULTIPLIER = 0.8f private val LOADING = MediaData(false, 0, null, null, null, null, null, - emptyList(), emptyList(), null, null, null) + emptyList(), emptyList(), null, null, null, null) + +fun isMediaNotification(sbn: StatusBarNotification): Boolean { + if (!sbn.notification.hasMediaSession()) { + return false + } + val notificationStyle = sbn.notification.notificationStyle + if (Notification.DecoratedMediaCustomViewStyle::class.java.equals(notificationStyle) || + Notification.MediaStyle::class.java.equals(notificationStyle)) { + return true + } + return false +} /** * A class that facilitates management and loading of Media Data, ready for binding. @@ -65,14 +77,14 @@ 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() private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() fun onNotificationAdded(key: String, sbn: StatusBarNotification) { - if (isMediaNotification(sbn)) { + if (Utils.useQsMediaPlayer(context) && isMediaNotification(sbn)) { if (!mediaEntries.containsKey(key)) { mediaEntries.put(key, LOADING) } @@ -201,10 +213,10 @@ 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)) + notif.contentIntent, null)) } } @@ -270,25 +282,10 @@ class MediaDataManager @Inject constructor( } } - private fun isMediaNotification(sbn: StatusBarNotification): Boolean { - if (!Utils.useQsMediaPlayer(context)) { - return false - } - if (!sbn.notification.hasMediaSession()) { - return false - } - val notificationStyle = sbn.notification.notificationStyle - if (Notification.DecoratedMediaCustomViewStyle::class.java.equals(notificationStyle) || - Notification.MediaStyle::class.java.equals(notificationStyle)) { - return true - } - return false - } - /** * 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/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt new file mode 100644 index 000000000000..2d16e2930365 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt @@ -0,0 +1,117 @@ +/* + * 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.Context +import android.service.notification.StatusBarNotification +import com.android.settingslib.media.LocalMediaManager +import com.android.settingslib.media.MediaDevice +import com.android.systemui.dagger.qualifiers.Main +import java.util.concurrent.Executor +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Provides information about the route (ie. device) where playback is occurring. + */ +@Singleton +class MediaDeviceManager @Inject constructor( + private val context: Context, + private val localMediaManagerFactory: LocalMediaManagerFactory, + private val featureFlag: MediaFeatureFlag, + @Main private val fgExecutor: Executor +) { + private val listeners: MutableSet<Listener> = mutableSetOf() + private val entries: MutableMap<String, Token> = mutableMapOf() + + /** + * Add a listener for changes to the media route (ie. device). + */ + fun addListener(listener: Listener) = listeners.add(listener) + + /** + * Remove a listener that has been registered with addListener. + */ + fun removeListener(listener: Listener) = listeners.remove(listener) + + fun onNotificationAdded(key: String, sbn: StatusBarNotification) { + if (featureFlag.enabled && isMediaNotification(sbn)) { + var tok = entries[key] + if (tok == null) { + tok = Token(key, localMediaManagerFactory.create(sbn.packageName)) + entries[key] = tok + tok.start() + } + } else { + onNotificationRemoved(key) + } + } + + fun onNotificationRemoved(key: String) { + val token = entries.remove(key) + token?.stop() + token?.let { + listeners.forEach { + it.onKeyRemoved(key) + } + } + } + + private fun processDevice(key: String, device: MediaDevice?) { + val data = MediaDeviceData(device?.icon, device?.name) + listeners.forEach { + it.onMediaDeviceChanged(key, data) + } + } + + interface Listener { + /** Called when the route has changed for a given notification. */ + fun onMediaDeviceChanged(key: String, data: MediaDeviceData?) + /** Called when the notification was removed. */ + fun onKeyRemoved(key: String) + } + + private inner class Token( + val key: String, + val localMediaManager: LocalMediaManager + ) : LocalMediaManager.DeviceCallback { + private var current: MediaDevice? = null + set(value) { + if (value != field) { + field = value + processDevice(key, value) + } + } + fun start() { + localMediaManager.registerCallback(this) + localMediaManager.startScan() + current = localMediaManager.getCurrentConnectedDevice() + } + fun stop() { + localMediaManager.stopScan() + localMediaManager.unregisterCallback(this) + } + override fun onDeviceListUpdate(devices: List<MediaDevice>?) = fgExecutor.execute { + current = localMediaManager.getCurrentConnectedDevice() + } + override fun onSelectedDeviceStateChanged(device: MediaDevice, state: Int) { + fgExecutor.execute { + current = device + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaFeatureFlag.kt b/packages/SystemUI/src/com/android/systemui/media/MediaFeatureFlag.kt new file mode 100644 index 000000000000..75eb33da64d8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/MediaFeatureFlag.kt @@ -0,0 +1,29 @@ +/* + * 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.Context +import com.android.systemui.util.Utils +import javax.inject.Inject + +/** + * Provides access to the current value of the feature flag. + */ +class MediaFeatureFlag @Inject constructor(private val context: Context) { + val enabled + get() = Utils.useQsMediaPlayer(context) +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index 6b1c520db7b1..3e7661ac0dd9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -26,6 +26,7 @@ import android.view.ViewGroup import android.view.ViewGroupOverlay import com.android.systemui.Interpolators import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.stack.StackStateAnimator @@ -46,7 +47,8 @@ class MediaHierarchyManager @Inject constructor( private val keyguardStateController: KeyguardStateController, private val bypassController: KeyguardBypassController, private val mediaViewManager: MediaViewManager, - private val mediaMeasurementProvider: MediaMeasurementManager + private val mediaMeasurementProvider: MediaMeasurementManager, + private val notifLockscreenUserManager: NotificationLockscreenUserManager ) { /** * The root overlay of the hierarchy. This is where the media notification is attached to @@ -56,7 +58,7 @@ class MediaHierarchyManager @Inject constructor( private var rootOverlay: ViewGroupOverlay? = null private lateinit var currentState: MediaState private val mediaCarousel - get() = mediaViewManager.mediaCarousel + get() = mediaViewManager.mediaCarousel private var animationStartState: MediaState? = null private var statusbarState: Int = statusBarStateController.state private var animator = ValueAnimator.ofFloat(0.0f, 1.0f).apply { @@ -136,9 +138,9 @@ class MediaHierarchyManager @Inject constructor( * * @return the hostView associated with this location */ - fun register(mediaObject: MediaHost) : ViewGroup { + fun register(mediaObject: MediaHost): ViewGroup { val viewHost = createUniqueObjectHost(mediaObject) - mediaObject.hostView = viewHost; + mediaObject.hostView = viewHost mediaHosts[mediaObject.location] = mediaObject if (mediaObject.location == desiredLocation) { // In case we are overriding a view that is already visible, make sure we attach it @@ -155,7 +157,7 @@ class MediaHierarchyManager @Inject constructor( private fun createUniqueObjectHost(host: MediaHost): UniqueObjectHostView { val viewHost = UniqueObjectHostView(context) viewHost.measurementCache = mediaMeasurementProvider.obtainCache(host) - viewHost.onMeasureListener = { input -> + viewHost.onMeasureListener = { input -> if (host.location == desiredLocation) { // Measurement of the currently active player is happening, Let's make // sure the player width is up to date @@ -215,8 +217,8 @@ class MediaHierarchyManager @Inject constructor( applyTargetStateIfNotAnimating() } else if (animate) { animator.cancel() - if (currentAttachmentLocation == IN_OVERLAY - || !previousHost.hostView.isAttachedToWindow) { + if (currentAttachmentLocation == IN_OVERLAY || + !previousHost.hostView.isAttachedToWindow) { // Let's animate to the new position, starting from the current position // We also go in here in case the view was detached, since the bounds wouldn't // be correct anymore @@ -237,10 +239,10 @@ class MediaHierarchyManager @Inject constructor( @MediaLocation currentLocation: Int, @MediaLocation previousLocation: Int ): Boolean { - if (currentLocation == LOCATION_QQS - && previousLocation == LOCATION_LOCKSCREEN - && (statusBarStateController.leaveOpenOnKeyguardHide() - || statusbarState == StatusBarState.SHADE_LOCKED)) { + if (currentLocation == LOCATION_QQS && + previousLocation == LOCATION_LOCKSCREEN && + (statusBarStateController.leaveOpenOnKeyguardHide() || + statusbarState == StatusBarState.SHADE_LOCKED)) { // Usually listening to the isShown is enough to determine this, but there is some // non-trivial reattaching logic happening that will make the view not-shown earlier return true @@ -251,10 +253,9 @@ class MediaHierarchyManager @Inject constructor( private fun adjustAnimatorForTransition(desiredLocation: Int, previousLocation: Int) { val (animDuration, delay) = getAnimationParams(previousLocation, desiredLocation) animator.apply { - duration = animDuration + duration = animDuration startDelay = delay } - } private fun getAnimationParams(previousLocation: Int, desiredLocation: Int): Pair<Long, Long> { @@ -262,8 +263,8 @@ class MediaHierarchyManager @Inject constructor( var delay = 0L if (previousLocation == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QQS) { // Going to the full shade, let's adjust the animation duration - if (statusbarState == StatusBarState.SHADE - && keyguardStateController.isKeyguardFadingAway) { + if (statusbarState == StatusBarState.SHADE && + keyguardStateController.isKeyguardFadingAway) { delay = keyguardStateController.keyguardFadingAwayDelay } animDuration = StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE.toLong() @@ -301,12 +302,12 @@ class MediaHierarchyManager @Inject constructor( /** * @return true if this transformation is guided by an external progress like a finger */ - private fun isCurrentlyInGuidedTransformation() : Boolean { + private fun isCurrentlyInGuidedTransformation(): Boolean { return getTransformationProgress() >= 0 } /** - * @return the current transformation progress if we're in a guided transformation and -1 + * @return the current transformation progress if we're in a guided transformation and -1 * otherwise */ private fun getTransformationProgress(): Float { @@ -377,19 +378,20 @@ class MediaHierarchyManager @Inject constructor( } private fun isTransitionRunning(): Boolean { - return isCurrentlyInGuidedTransformation() && getTransformationProgress() != 1.0f - || animator.isRunning + return isCurrentlyInGuidedTransformation() && getTransformationProgress() != 1.0f || + animator.isRunning } @MediaLocation - private fun calculateLocation() : Int { - val onLockscreen = (!bypassController.bypassEnabled - && (statusbarState == StatusBarState.KEYGUARD - || statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER)) + private fun calculateLocation(): Int { + val onLockscreen = (!bypassController.bypassEnabled && + (statusbarState == StatusBarState.KEYGUARD || + statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER)) + val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications() return when { qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS qsExpansion > 0.4f && onLockscreen -> LOCATION_QS - onLockscreen -> LOCATION_LOCKSCREEN + onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN else -> LOCATION_QQS } } 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/MediaViewManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt index 8db9dcc1ecec..17e8404fe705 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt @@ -6,9 +6,6 @@ import android.view.View import android.view.ViewGroup import android.widget.HorizontalScrollView import android.widget.LinearLayout -import com.android.settingslib.bluetooth.LocalBluetoothManager -import com.android.settingslib.media.InfoMediaManager -import com.android.settingslib.media.LocalMediaManager import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main @@ -30,10 +27,9 @@ class MediaViewManager @Inject constructor( private val context: Context, @Main private val foregroundExecutor: Executor, @Background private val backgroundExecutor: DelayableExecutor, - private val localBluetoothManager: LocalBluetoothManager?, private val visualStabilityManager: VisualStabilityManager, private val activityStarter: ActivityStarter, - mediaManager: MediaDataManager + mediaManager: MediaDataCombineLatest ) { private var playerWidth: Int = 0 private var playerWidthPlusPadding: Int = 0 @@ -42,7 +38,7 @@ class MediaViewManager @Inject constructor( val mediaCarousel: HorizontalScrollView private val mediaContent: ViewGroup private val mediaPlayers: MutableMap<String, MediaControlPanel> = mutableMapOf() - private val visualStabilityCallback : VisualStabilityManager.Callback + private val visualStabilityCallback: VisualStabilityManager.Callback private var activeMediaIndex: Int = 0 private var needsReordering: Boolean = false private var scrollIntoCurrentMedia: Int = 0 @@ -151,15 +147,8 @@ class MediaViewManager @Inject constructor( private fun updateView(key: String, data: MediaData) { var existingPlayer = mediaPlayers[key] if (existingPlayer == null) { - // Set up listener for device changes - // TODO: integrate with MediaTransferManager? - val imm = InfoMediaManager(context, data.packageName, - null /* notification */, localBluetoothManager) - val routeManager = LocalMediaManager(context, localBluetoothManager, - imm, data.packageName) - - existingPlayer = MediaControlPanel(context, routeManager, foregroundExecutor, - backgroundExecutor, activityStarter) + existingPlayer = MediaControlPanel(context, foregroundExecutor, backgroundExecutor, + activityStarter) existingPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), mediaContent)) mediaPlayers[key] = existingPlayer diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt index 571e18d0ff20..764dbe6d428f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt @@ -60,6 +60,17 @@ class PlayerViewHolder private constructor(itemView: View) { val action3 = itemView.requireViewById<ImageButton>(R.id.action3) val action4 = itemView.requireViewById<ImageButton>(R.id.action4) + init { + (background.background as IlluminationDrawable).let { + it.setupTouch(seamless, player) + it.setupTouch(action0, player) + it.setupTouch(action1, player) + it.setupTouch(action2, player) + it.setupTouch(action3, player) + it.setupTouch(action4, player) + } + } + fun getAction(id: Int): ImageButton { return when (id) { R.id.action0 -> action0 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/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index 1982227032cf..69bad80082e4 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -21,6 +21,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS; import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS; import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS; +import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS; import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ALLOW_TIMEOUT; @@ -68,6 +69,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.LinearLayout; @@ -220,6 +222,29 @@ public class PipMenuActivity extends Activity { // Hide without an animation. getWindow().setExitTransition(null); + + initAccessibility(); + } + + private void initAccessibility() { + getWindow().getDecorView().setAccessibilityDelegate(new View.AccessibilityDelegate() { + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + String label = getResources().getString(R.string.pip_menu_title); + info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, label)); + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) { + Message m = Message.obtain(); + m.what = PipMenuActivityController.MESSAGE_SHOW_MENU; + sendMessage(m, "Could not notify controller to show PIP menu"); + } + return super.performAccessibilityAction(host, action, args); + } + }); } @Override 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/power/InattentiveSleepWarningView.java b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java index cf1bc7dc115d..1ed98c0a8f90 100644 --- a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java +++ b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java @@ -119,7 +119,7 @@ public class InattentiveSleepWarningView extends FrameLayout { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, + WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT); lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; 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/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index cdd1280dd86c..379bb9d247db 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -18,15 +18,10 @@ package com.android.systemui.stackdivider; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; -import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.content.Context; @@ -36,15 +31,11 @@ import android.os.Handler; import android.provider.Settings; import android.util.Slog; import android.view.LayoutInflater; -import android.view.SurfaceControl; import android.view.View; -import android.window.TaskOrganizer; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import android.window.WindowOrganizer; -import androidx.annotation.Nullable; - import com.android.internal.policy.DividerSnapAlgorithm; import com.android.systemui.R; import com.android.systemui.SystemUI; @@ -81,7 +72,6 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, static final boolean DEBUG = false; static final int DEFAULT_APP_TRANSITION_DURATION = 336; - static final float ADJUSTED_NONFOCUS_DIM = 0.3f; private final Optional<Lazy<Recents>> mRecentsOptionalLazy; @@ -139,303 +129,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } }; - private class DividerImeController implements DisplayImeController.ImePositionProcessor { - /** - * These are the y positions of the top of the IME surface when it is hidden and when it is - * shown respectively. These are NOT necessarily the top of the visible IME itself. - */ - private int mHiddenTop = 0; - private int mShownTop = 0; - - // The following are target states (what we are curretly animating towards). - /** - * {@code true} if, at the end of the animation, the split task positions should be - * adjusted by height of the IME. This happens when the secondary split is the IME target. - */ - private boolean mTargetAdjusted = false; - /** - * {@code true} if, at the end of the animation, the IME should be shown/visible - * regardless of what has focus. - */ - private boolean mTargetShown = false; - private float mTargetPrimaryDim = 0.f; - private float mTargetSecondaryDim = 0.f; - - // The following are the current (most recent) states set during animation - /** {@code true} if the secondary split has IME focus. */ - private boolean mSecondaryHasFocus = false; - /** The dimming currently applied to the primary/secondary splits. */ - private float mLastPrimaryDim = 0.f; - private float mLastSecondaryDim = 0.f; - /** The most recent y position of the top of the IME surface */ - private int mLastAdjustTop = -1; - - // The following are states reached last time an animation fully completed. - /** {@code true} if the IME was shown/visible by the last-completed animation. */ - private boolean mImeWasShown = false; - /** {@code true} if the split positions were adjusted by the last-completed animation. */ - private boolean mAdjusted = false; - - /** - * When some aspect of split-screen needs to animate independent from the IME, - * this will be non-null and control split animation. - */ - @Nullable - private ValueAnimator mAnimation = null; - - private boolean mPaused = true; - private boolean mPausedTargetAdjusted = false; - - private boolean getSecondaryHasFocus(int displayId) { - WindowContainerToken imeSplit = TaskOrganizer.getImeTarget(displayId); - return imeSplit != null - && (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder()); - } - - private void updateDimTargets() { - final boolean splitIsVisible = !mView.isHidden(); - mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible) - ? ADJUSTED_NONFOCUS_DIM : 0.f; - mTargetSecondaryDim = (!mSecondaryHasFocus && mTargetShown && splitIsVisible) - ? ADJUSTED_NONFOCUS_DIM : 0.f; - } - - @Override - public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop, - boolean imeShouldShow, SurfaceControl.Transaction t) { - if (!isDividerVisible()) { - return; - } - final boolean splitIsVisible = !mView.isHidden(); - mSecondaryHasFocus = getSecondaryHasFocus(displayId); - final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus - && !mSplitLayout.mDisplayLayout.isLandscape(); - mHiddenTop = hiddenTop; - mShownTop = shownTop; - mTargetShown = imeShouldShow; - if (mLastAdjustTop < 0) { - mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop; - } else if (mLastAdjustTop != (imeShouldShow ? mShownTop : mHiddenTop)) { - if (mTargetAdjusted != targetAdjusted && targetAdjusted == mAdjusted) { - // Check for an "interruption" of an existing animation. In this case, we - // need to fake-flip the last-known state direction so that the animation - // completes in the other direction. - mAdjusted = mTargetAdjusted; - } else if (targetAdjusted && mTargetAdjusted && mAdjusted) { - // Already fully adjusted for IME, but IME height has changed; so, force-start - // an async animation to the new IME height. - mAdjusted = false; - } - } - if (mPaused) { - mPausedTargetAdjusted = targetAdjusted; - if (DEBUG) Slog.d(TAG, " ime starting but paused " + dumpState()); - return; - } - mTargetAdjusted = targetAdjusted; - updateDimTargets(); - if (DEBUG) Slog.d(TAG, " ime starting. vis:" + splitIsVisible + " " + dumpState()); - if (mAnimation != null || (mImeWasShown && imeShouldShow - && mTargetAdjusted != mAdjusted)) { - // We need to animate adjustment independently of the IME position, so - // start our own animation to drive adjustment. This happens when a - // different split's editor has gained focus while the IME is still visible. - startAsyncAnimation(); - } - if (splitIsVisible) { - // If split is hidden, we don't want to trigger any relayouts that would cause the - // divider to show again. - updateImeAdjustState(); - } - } - - private void updateImeAdjustState() { - // Reposition the server's secondary split position so that it evaluates - // insets properly. - WindowContainerTransaction wct = new WindowContainerTransaction(); - if (mTargetAdjusted) { - mSplitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop); - wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mAdjustedSecondary); - // "Freeze" the configuration size so that the app doesn't get a config - // or relaunch. This is required because normally nav-bar contributes - // to configuration bounds (via nondecorframe). - Rect adjustAppBounds = new Rect(mSplits.mSecondary.configuration - .windowConfiguration.getAppBounds()); - adjustAppBounds.offset(0, mSplitLayout.mAdjustedSecondary.top - - mSplitLayout.mSecondary.top); - wct.setAppBounds(mSplits.mSecondary.token, adjustAppBounds); - wct.setScreenSizeDp(mSplits.mSecondary.token, - mSplits.mSecondary.configuration.screenWidthDp, - mSplits.mSecondary.configuration.screenHeightDp); - - wct.setBounds(mSplits.mPrimary.token, mSplitLayout.mAdjustedPrimary); - adjustAppBounds = new Rect(mSplits.mPrimary.configuration - .windowConfiguration.getAppBounds()); - adjustAppBounds.offset(0, mSplitLayout.mAdjustedPrimary.top - - mSplitLayout.mPrimary.top); - wct.setAppBounds(mSplits.mPrimary.token, adjustAppBounds); - wct.setScreenSizeDp(mSplits.mPrimary.token, - mSplits.mPrimary.configuration.screenWidthDp, - mSplits.mPrimary.configuration.screenHeightDp); - } else { - wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mSecondary); - wct.setAppBounds(mSplits.mSecondary.token, null); - wct.setScreenSizeDp(mSplits.mSecondary.token, - SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); - wct.setBounds(mSplits.mPrimary.token, mSplitLayout.mPrimary); - wct.setAppBounds(mSplits.mPrimary.token, null); - wct.setScreenSizeDp(mSplits.mPrimary.token, - SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); - } - - WindowOrganizer.applyTransaction(wct); - - // Update all the adjusted-for-ime states - if (!mPaused) { - mView.setAdjustedForIme(mTargetShown, mTargetShown - ? DisplayImeController.ANIMATION_DURATION_SHOW_MS - : DisplayImeController.ANIMATION_DURATION_HIDE_MS); - } - setAdjustedForIme(mTargetShown && !mPaused); - } - - @Override - public void onImePositionChanged(int displayId, int imeTop, - SurfaceControl.Transaction t) { - if (mAnimation != null || !isDividerVisible() || mPaused) { - // Not synchronized with IME anymore, so return. - return; - } - final float fraction = ((float) imeTop - mHiddenTop) / (mShownTop - mHiddenTop); - final float progress = mTargetShown ? fraction : 1.f - fraction; - onProgress(progress, t); - } - - @Override - public void onImeEndPositioning(int displayId, boolean cancelled, - SurfaceControl.Transaction t) { - if (mAnimation != null || !isDividerVisible() || mPaused) { - // Not synchronized with IME anymore, so return. - return; - } - onEnd(cancelled, t); - } - - private void onProgress(float progress, SurfaceControl.Transaction t) { - if (mTargetAdjusted != mAdjusted && !mPaused) { - final float fraction = mTargetAdjusted ? progress : 1.f - progress; - mLastAdjustTop = (int) (fraction * mShownTop + (1.f - fraction) * mHiddenTop); - mSplitLayout.updateAdjustedBounds(mLastAdjustTop, mHiddenTop, mShownTop); - mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary, - mSplitLayout.mAdjustedSecondary); - } - final float invProg = 1.f - progress; - mView.setResizeDimLayer(t, true /* primary */, - mLastPrimaryDim * invProg + progress * mTargetPrimaryDim); - mView.setResizeDimLayer(t, false /* primary */, - mLastSecondaryDim * invProg + progress * mTargetSecondaryDim); - } - - private void onEnd(boolean cancelled, SurfaceControl.Transaction t) { - if (!cancelled) { - onProgress(1.f, t); - mAdjusted = mTargetAdjusted; - mImeWasShown = mTargetShown; - mLastAdjustTop = mAdjusted ? mShownTop : mHiddenTop; - mLastPrimaryDim = mTargetPrimaryDim; - mLastSecondaryDim = mTargetSecondaryDim; - } - } - - private void startAsyncAnimation() { - if (mAnimation != null) { - mAnimation.cancel(); - } - mAnimation = ValueAnimator.ofFloat(0.f, 1.f); - mAnimation.setDuration(DisplayImeController.ANIMATION_DURATION_SHOW_MS); - if (mTargetAdjusted != mAdjusted) { - final float fraction = - ((float) mLastAdjustTop - mHiddenTop) / (mShownTop - mHiddenTop); - final float progress = mTargetAdjusted ? fraction : 1.f - fraction; - mAnimation.setCurrentFraction(progress); - } - - mAnimation.addUpdateListener(animation -> { - SurfaceControl.Transaction t = mTransactionPool.acquire(); - float value = (float) animation.getAnimatedValue(); - onProgress(value, t); - t.apply(); - mTransactionPool.release(t); - }); - mAnimation.setInterpolator(DisplayImeController.INTERPOLATOR); - mAnimation.addListener(new AnimatorListenerAdapter() { - private boolean mCancel = false; - @Override - public void onAnimationCancel(Animator animation) { - mCancel = true; - } - @Override - public void onAnimationEnd(Animator animation) { - SurfaceControl.Transaction t = mTransactionPool.acquire(); - onEnd(mCancel, t); - t.apply(); - mTransactionPool.release(t); - mAnimation = null; - } - }); - mAnimation.start(); - } - - private String dumpState() { - return "top:" + mHiddenTop + "->" + mShownTop - + " adj:" + mAdjusted + "->" + mTargetAdjusted + "(" + mLastAdjustTop + ")" - + " shw:" + mImeWasShown + "->" + mTargetShown - + " dims:" + mLastPrimaryDim + "," + mLastSecondaryDim - + "->" + mTargetPrimaryDim + "," + mTargetSecondaryDim - + " shf:" + mSecondaryHasFocus + " desync:" + (mAnimation != null) - + " paus:" + mPaused + "[" + mPausedTargetAdjusted + "]"; - } - - /** Completely aborts/resets adjustment state */ - public void pause(int displayId) { - if (DEBUG) Slog.d(TAG, "ime pause posting " + dumpState()); - mHandler.post(() -> { - if (DEBUG) Slog.d(TAG, "ime pause run posted " + dumpState()); - if (mPaused) { - return; - } - mPaused = true; - mPausedTargetAdjusted = mTargetAdjusted; - mTargetAdjusted = false; - mTargetPrimaryDim = mTargetSecondaryDim = 0.f; - updateImeAdjustState(); - startAsyncAnimation(); - if (mAnimation != null) { - mAnimation.end(); - } - }); - } - - public void resume(int displayId) { - if (DEBUG) Slog.d(TAG, "ime resume posting " + dumpState()); - mHandler.post(() -> { - if (DEBUG) Slog.d(TAG, "ime resume run posted " + dumpState()); - if (!mPaused) { - return; - } - mPaused = false; - mTargetAdjusted = mPausedTargetAdjusted; - updateDimTargets(); - if ((mTargetAdjusted != mAdjusted) && !mMinimized && mView != null) { - // End unminimize animations since they conflict with adjustment animations. - mView.finishAnimations(); - } - updateImeAdjustState(); - startAsyncAnimation(); - }); - } - } - private final DividerImeController mImePositionProcessor = new DividerImeController(); + private final DividerImeController mImePositionProcessor; private TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() { @Override @@ -465,6 +159,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, mRecentsOptionalLazy = recentsOptionalLazy; mForcedResizableController = new ForcedResizableInfoActivityController(context, this); mTransactionPool = transactionPool; + mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler); } @Override @@ -830,6 +525,10 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } } + SplitDisplayLayout getSplitLayout() { + return mSplitLayout; + } + /** @return the container token for the secondary split root task. */ public WindowContainerToken getSecondaryRoot() { if (mSplits == null || mSplits.mSecondary == null) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java new file mode 100644 index 000000000000..533fd5fa6352 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java @@ -0,0 +1,368 @@ +/* + * 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.stackdivider; + +import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; +import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.graphics.Rect; +import android.os.Handler; +import android.util.Slog; +import android.view.SurfaceControl; +import android.window.TaskOrganizer; +import android.window.WindowContainerToken; +import android.window.WindowContainerTransaction; +import android.window.WindowOrganizer; + +import androidx.annotation.Nullable; + +import com.android.systemui.TransactionPool; +import com.android.systemui.wm.DisplayImeController; + +class DividerImeController implements DisplayImeController.ImePositionProcessor { + private static final String TAG = "DividerImeController"; + private static final boolean DEBUG = Divider.DEBUG; + + private static final float ADJUSTED_NONFOCUS_DIM = 0.3f; + + private final SplitScreenTaskOrganizer mSplits; + private final TransactionPool mTransactionPool; + private final Handler mHandler; + + /** + * These are the y positions of the top of the IME surface when it is hidden and when it is + * shown respectively. These are NOT necessarily the top of the visible IME itself. + */ + private int mHiddenTop = 0; + private int mShownTop = 0; + + // The following are target states (what we are curretly animating towards). + /** + * {@code true} if, at the end of the animation, the split task positions should be + * adjusted by height of the IME. This happens when the secondary split is the IME target. + */ + private boolean mTargetAdjusted = false; + /** + * {@code true} if, at the end of the animation, the IME should be shown/visible + * regardless of what has focus. + */ + private boolean mTargetShown = false; + private float mTargetPrimaryDim = 0.f; + private float mTargetSecondaryDim = 0.f; + + // The following are the current (most recent) states set during animation + /** {@code true} if the secondary split has IME focus. */ + private boolean mSecondaryHasFocus = false; + /** The dimming currently applied to the primary/secondary splits. */ + private float mLastPrimaryDim = 0.f; + private float mLastSecondaryDim = 0.f; + /** The most recent y position of the top of the IME surface */ + private int mLastAdjustTop = -1; + + // The following are states reached last time an animation fully completed. + /** {@code true} if the IME was shown/visible by the last-completed animation. */ + private boolean mImeWasShown = false; + /** {@code true} if the split positions were adjusted by the last-completed animation. */ + private boolean mAdjusted = false; + + /** + * When some aspect of split-screen needs to animate independent from the IME, + * this will be non-null and control split animation. + */ + @Nullable + private ValueAnimator mAnimation = null; + + private boolean mPaused = true; + private boolean mPausedTargetAdjusted = false; + + DividerImeController(SplitScreenTaskOrganizer splits, TransactionPool pool, Handler handler) { + mSplits = splits; + mTransactionPool = pool; + mHandler = handler; + } + + private DividerView getView() { + return mSplits.mDivider.getView(); + } + + private SplitDisplayLayout getLayout() { + return mSplits.mDivider.getSplitLayout(); + } + + private boolean isDividerVisible() { + return mSplits.mDivider.isDividerVisible(); + } + + private boolean getSecondaryHasFocus(int displayId) { + WindowContainerToken imeSplit = TaskOrganizer.getImeTarget(displayId); + return imeSplit != null + && (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder()); + } + + private void updateDimTargets() { + final boolean splitIsVisible = !getView().isHidden(); + mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible) + ? ADJUSTED_NONFOCUS_DIM : 0.f; + mTargetSecondaryDim = (!mSecondaryHasFocus && mTargetShown && splitIsVisible) + ? ADJUSTED_NONFOCUS_DIM : 0.f; + } + + @Override + public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop, + boolean imeShouldShow, SurfaceControl.Transaction t) { + if (!isDividerVisible()) { + return; + } + final boolean splitIsVisible = !getView().isHidden(); + mSecondaryHasFocus = getSecondaryHasFocus(displayId); + final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus + && !getLayout().mDisplayLayout.isLandscape(); + mHiddenTop = hiddenTop; + mShownTop = shownTop; + mTargetShown = imeShouldShow; + if (mLastAdjustTop < 0) { + mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop; + } else if (mLastAdjustTop != (imeShouldShow ? mShownTop : mHiddenTop)) { + if (mTargetAdjusted != targetAdjusted && targetAdjusted == mAdjusted) { + // Check for an "interruption" of an existing animation. In this case, we + // need to fake-flip the last-known state direction so that the animation + // completes in the other direction. + mAdjusted = mTargetAdjusted; + } else if (targetAdjusted && mTargetAdjusted && mAdjusted) { + // Already fully adjusted for IME, but IME height has changed; so, force-start + // an async animation to the new IME height. + mAdjusted = false; + } + } + if (mPaused) { + mPausedTargetAdjusted = targetAdjusted; + if (DEBUG) Slog.d(TAG, " ime starting but paused " + dumpState()); + return; + } + mTargetAdjusted = targetAdjusted; + updateDimTargets(); + if (DEBUG) Slog.d(TAG, " ime starting. vis:" + splitIsVisible + " " + dumpState()); + if (mAnimation != null || (mImeWasShown && imeShouldShow + && mTargetAdjusted != mAdjusted)) { + // We need to animate adjustment independently of the IME position, so + // start our own animation to drive adjustment. This happens when a + // different split's editor has gained focus while the IME is still visible. + startAsyncAnimation(); + } + if (splitIsVisible) { + // If split is hidden, we don't want to trigger any relayouts that would cause the + // divider to show again. + updateImeAdjustState(); + } + } + + private void updateImeAdjustState() { + // Reposition the server's secondary split position so that it evaluates + // insets properly. + WindowContainerTransaction wct = new WindowContainerTransaction(); + final SplitDisplayLayout splitLayout = getLayout(); + if (mTargetAdjusted) { + splitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop); + wct.setBounds(mSplits.mSecondary.token, splitLayout.mAdjustedSecondary); + // "Freeze" the configuration size so that the app doesn't get a config + // or relaunch. This is required because normally nav-bar contributes + // to configuration bounds (via nondecorframe). + Rect adjustAppBounds = new Rect(mSplits.mSecondary.configuration + .windowConfiguration.getAppBounds()); + adjustAppBounds.offset(0, splitLayout.mAdjustedSecondary.top + - splitLayout.mSecondary.top); + wct.setAppBounds(mSplits.mSecondary.token, adjustAppBounds); + wct.setScreenSizeDp(mSplits.mSecondary.token, + mSplits.mSecondary.configuration.screenWidthDp, + mSplits.mSecondary.configuration.screenHeightDp); + + wct.setBounds(mSplits.mPrimary.token, splitLayout.mAdjustedPrimary); + adjustAppBounds = new Rect(mSplits.mPrimary.configuration + .windowConfiguration.getAppBounds()); + adjustAppBounds.offset(0, splitLayout.mAdjustedPrimary.top + - splitLayout.mPrimary.top); + wct.setAppBounds(mSplits.mPrimary.token, adjustAppBounds); + wct.setScreenSizeDp(mSplits.mPrimary.token, + mSplits.mPrimary.configuration.screenWidthDp, + mSplits.mPrimary.configuration.screenHeightDp); + } else { + wct.setBounds(mSplits.mSecondary.token, splitLayout.mSecondary); + wct.setAppBounds(mSplits.mSecondary.token, null); + wct.setScreenSizeDp(mSplits.mSecondary.token, + SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); + wct.setBounds(mSplits.mPrimary.token, splitLayout.mPrimary); + wct.setAppBounds(mSplits.mPrimary.token, null); + wct.setScreenSizeDp(mSplits.mPrimary.token, + SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); + } + + WindowOrganizer.applyTransaction(wct); + + // Update all the adjusted-for-ime states + if (!mPaused) { + final DividerView view = getView(); + if (view != null) { + view.setAdjustedForIme(mTargetShown, mTargetShown + ? DisplayImeController.ANIMATION_DURATION_SHOW_MS + : DisplayImeController.ANIMATION_DURATION_HIDE_MS); + } + } + mSplits.mDivider.setAdjustedForIme(mTargetShown && !mPaused); + } + + @Override + public void onImePositionChanged(int displayId, int imeTop, + SurfaceControl.Transaction t) { + if (mAnimation != null || !isDividerVisible() || mPaused) { + // Not synchronized with IME anymore, so return. + return; + } + final float fraction = ((float) imeTop - mHiddenTop) / (mShownTop - mHiddenTop); + final float progress = mTargetShown ? fraction : 1.f - fraction; + onProgress(progress, t); + } + + @Override + public void onImeEndPositioning(int displayId, boolean cancelled, + SurfaceControl.Transaction t) { + if (mAnimation != null || !isDividerVisible() || mPaused) { + // Not synchronized with IME anymore, so return. + return; + } + onEnd(cancelled, t); + } + + private void onProgress(float progress, SurfaceControl.Transaction t) { + final DividerView view = getView(); + if (mTargetAdjusted != mAdjusted && !mPaused) { + final SplitDisplayLayout splitLayout = getLayout(); + final float fraction = mTargetAdjusted ? progress : 1.f - progress; + mLastAdjustTop = (int) (fraction * mShownTop + (1.f - fraction) * mHiddenTop); + splitLayout.updateAdjustedBounds(mLastAdjustTop, mHiddenTop, mShownTop); + view.resizeSplitSurfaces(t, splitLayout.mAdjustedPrimary, + splitLayout.mAdjustedSecondary); + } + final float invProg = 1.f - progress; + view.setResizeDimLayer(t, true /* primary */, + mLastPrimaryDim * invProg + progress * mTargetPrimaryDim); + view.setResizeDimLayer(t, false /* primary */, + mLastSecondaryDim * invProg + progress * mTargetSecondaryDim); + } + + private void onEnd(boolean cancelled, SurfaceControl.Transaction t) { + if (!cancelled) { + onProgress(1.f, t); + mAdjusted = mTargetAdjusted; + mImeWasShown = mTargetShown; + mLastAdjustTop = mAdjusted ? mShownTop : mHiddenTop; + mLastPrimaryDim = mTargetPrimaryDim; + mLastSecondaryDim = mTargetSecondaryDim; + } + } + + private void startAsyncAnimation() { + if (mAnimation != null) { + mAnimation.cancel(); + } + mAnimation = ValueAnimator.ofFloat(0.f, 1.f); + mAnimation.setDuration(DisplayImeController.ANIMATION_DURATION_SHOW_MS); + if (mTargetAdjusted != mAdjusted) { + final float fraction = + ((float) mLastAdjustTop - mHiddenTop) / (mShownTop - mHiddenTop); + final float progress = mTargetAdjusted ? fraction : 1.f - fraction; + mAnimation.setCurrentFraction(progress); + } + + mAnimation.addUpdateListener(animation -> { + SurfaceControl.Transaction t = mTransactionPool.acquire(); + float value = (float) animation.getAnimatedValue(); + onProgress(value, t); + t.apply(); + mTransactionPool.release(t); + }); + mAnimation.setInterpolator(DisplayImeController.INTERPOLATOR); + mAnimation.addListener(new AnimatorListenerAdapter() { + private boolean mCancel = false; + @Override + public void onAnimationCancel(Animator animation) { + mCancel = true; + } + @Override + public void onAnimationEnd(Animator animation) { + SurfaceControl.Transaction t = mTransactionPool.acquire(); + onEnd(mCancel, t); + t.apply(); + mTransactionPool.release(t); + mAnimation = null; + } + }); + mAnimation.start(); + } + + private String dumpState() { + return "top:" + mHiddenTop + "->" + mShownTop + + " adj:" + mAdjusted + "->" + mTargetAdjusted + "(" + mLastAdjustTop + ")" + + " shw:" + mImeWasShown + "->" + mTargetShown + + " dims:" + mLastPrimaryDim + "," + mLastSecondaryDim + + "->" + mTargetPrimaryDim + "," + mTargetSecondaryDim + + " shf:" + mSecondaryHasFocus + " desync:" + (mAnimation != null) + + " paus:" + mPaused + "[" + mPausedTargetAdjusted + "]"; + } + + /** Completely aborts/resets adjustment state */ + public void pause(int displayId) { + if (DEBUG) Slog.d(TAG, "ime pause posting " + dumpState()); + mHandler.post(() -> { + if (DEBUG) Slog.d(TAG, "ime pause run posted " + dumpState()); + if (mPaused) { + return; + } + mPaused = true; + mPausedTargetAdjusted = mTargetAdjusted; + mTargetAdjusted = false; + mTargetPrimaryDim = mTargetSecondaryDim = 0.f; + updateImeAdjustState(); + startAsyncAnimation(); + if (mAnimation != null) { + mAnimation.end(); + } + }); + } + + public void resume(int displayId) { + if (DEBUG) Slog.d(TAG, "ime resume posting " + dumpState()); + mHandler.post(() -> { + if (DEBUG) Slog.d(TAG, "ime resume run posted " + dumpState()); + if (!mPaused) { + return; + } + mPaused = false; + mTargetAdjusted = mPausedTargetAdjusted; + updateDimTargets(); + final DividerView view = getView(); + if ((mTargetAdjusted != mAdjusted) && !mSplits.mDivider.isMinimized() && view != null) { + // End unminimize animations since they conflict with adjustment animations. + view.finishAnimations(); + } + updateImeAdjustState(); + startAsyncAnimation(); + }); + } +} 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..8ed69d8fb982 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,8 +52,8 @@ 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.media.MediaDeviceManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.dagger.StatusBarModule; import com.android.systemui.statusbar.notification.NotificationEntryListener; @@ -70,6 +69,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 +79,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 +90,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 +107,7 @@ public class NotificationMediaManager implements Dumpable { } private final NotificationEntryManager mEntryManager; + private final MediaDataManager mMediaDataManager; @Nullable private Lazy<NotificationShadeWindowController> mNotificationShadeWindowController; @@ -117,7 +119,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 +132,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 +162,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,9 +217,10 @@ public class NotificationMediaManager implements Dumpable { NotificationEntryManager notificationEntryManager, MediaArtworkProcessor mediaArtworkProcessor, KeyguardBypassController keyguardBypassController, - @Main Executor mainExecutor, + @Main DelayableExecutor mainExecutor, DeviceConfigProxy deviceConfig, - MediaDataManager mediaDataManager) { + MediaDataManager mediaDataManager, + MediaDeviceManager mediaDeviceManager) { mContext = context; mMediaArtworkProcessor = mediaArtworkProcessor; mKeyguardBypassController = keyguardBypassController; @@ -205,17 +234,20 @@ public class NotificationMediaManager implements Dumpable { mNotificationShadeWindowController = notificationShadeWindowController; mEntryManager = notificationEntryManager; mMainExecutor = mainExecutor; + mMediaDataManager = mediaDataManager; notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override public void onPendingEntryAdded(NotificationEntry entry) { mediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn()); + mediaDeviceManager.onNotificationAdded(entry.getKey(), entry.getSbn()); } @Override public void onPreEntryUpdated(NotificationEntry entry) { mediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn()); + mediaDeviceManager.onNotificationAdded(entry.getKey(), entry.getSbn()); } @Override @@ -236,6 +268,7 @@ public class NotificationMediaManager implements Dumpable { int reason) { onNotificationRemoved(entry.getKey()); mediaDataManager.onNotificationRemoved(entry.getKey()); + mediaDeviceManager.onNotificationRemoved(entry.getKey()); } }); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 7aae724ae80a..d3819e5faa36 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -37,6 +37,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.ActivityLaunchAnimator import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK +import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.NotificationShadeWindowController import com.android.systemui.statusbar.phone.PanelExpansionListener import com.android.systemui.statusbar.phone.ScrimController @@ -60,6 +61,7 @@ class NotificationShadeDepthController @Inject constructor( private val choreographer: Choreographer, private val wallpaperManager: WallpaperManager, private val notificationShadeWindowController: NotificationShadeWindowController, + private val dozeParameters: DozeParameters, dumpManager: DumpManager ) : PanelExpansionListener, Dumpable { companion object { @@ -206,9 +208,12 @@ class NotificationShadeDepthController @Inject constructor( keyguardAnimator?.cancel() keyguardAnimator = ValueAnimator.ofFloat(1f, 0f).apply { - duration = keyguardStateController.keyguardFadingAwayDuration + // keyguardStateController.keyguardFadingAwayDuration might be zero when unlock by + // fingerprint due to there is no window container, see AppTransition#goodToGo. + // We use DozeParameters.wallpaperFadeOutDuration as an alternative. + duration = dozeParameters.wallpaperFadeOutDuration startDelay = keyguardStateController.keyguardFadingAwayDelay - interpolator = Interpolators.DECELERATE_QUINT + interpolator = Interpolators.FAST_OUT_SLOW_IN addUpdateListener { animation: ValueAnimator -> wakeAndUnlockBlurRadius = blurUtils.blurRadiusOfRatio(animation.animatedValue as Float) 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..ac2a9c118672 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -24,6 +24,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.media.MediaDataManager; +import com.android.systemui.media.MediaDeviceManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.ActionClickLogger; import com.android.systemui.statusbar.CommandQueue; @@ -47,6 +48,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,9 +100,10 @@ public interface StatusBarDependenciesModule { NotificationEntryManager notificationEntryManager, MediaArtworkProcessor mediaArtworkProcessor, KeyguardBypassController keyguardBypassController, - @Main Executor mainExecutor, + @Main DelayableExecutor mainExecutor, DeviceConfigProxy deviceConfigProxy, - MediaDataManager mediaDataManager) { + MediaDataManager mediaDataManager, + MediaDeviceManager mediaDeviceManager) { return new NotificationMediaManager( context, statusBarLazy, @@ -110,7 +113,8 @@ public interface StatusBarDependenciesModule { keyguardBypassController, mainExecutor, deviceConfigProxy, - mediaDataManager); + mediaDataManager, + mediaDeviceManager); } /** */ 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..adb51a5d959a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -573,16 +573,14 @@ public class NotificationEntryManager implements SystemClock.uptimeMillis()); mAllNotifications.add(entry); mLeakDetector.trackInstance(entry); - } - - abortExistingInflation(key, "addNotification"); - for (NotifCollectionListener listener : mNotifCollectionListeners) { - listener.onEntryBind(entry, notification); + for (NotifCollectionListener listener : mNotifCollectionListeners) { + listener.onEntryInit(entry); + } } for (NotifCollectionListener listener : mNotifCollectionListeners) { - listener.onEntryInit(entry); + listener.onEntryBind(entry, notification); } // Construct the expanded view. 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/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index fb5bfd88e3e1..d7365e6db935 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -398,11 +398,11 @@ public class NotifCollection implements Dumpable { if (entry == null) { // A new notification! entry = new NotificationEntry(sbn, ranking, SystemClock.uptimeMillis()); + mEventQueue.add(new InitEntryEvent(entry)); mEventQueue.add(new BindEntryEvent(entry, sbn)); mNotificationSet.put(sbn.getKey(), entry); mLogger.logNotifPosted(sbn.getKey()); - mEventQueue.add(new InitEntryEvent(entry)); mEventQueue.add(new EntryAddedEvent(entry)); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt index dc0b802129fc..bab2686c5c9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt @@ -74,11 +74,14 @@ open class NotificationRankingManager @Inject constructor( val aRank = a.ranking.rank val bRank = b.ranking.rank + val aIsFsn = a.isColorizedForegroundService() + val bIsFsn = b.isColorizedForegroundService() + val aPersonType = a.getPeopleNotificationType() val bPersonType = b.getPeopleNotificationType() - val aMedia = isImportantMedia(a) - val bMedia = isImportantMedia(b) + val aMedia = a.isImportantMedia() + val bMedia = b.isImportantMedia() val aSystemMax = a.isSystemMax() val bSystemMax = b.isSystemMax() @@ -92,7 +95,7 @@ open class NotificationRankingManager @Inject constructor( aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1 // Provide consistent ranking with headsUpManager aHeadsUp -> headsUpManager.compare(a, b) - + aIsFsn != bIsFsn -> if (aIsFsn) -1 else 1 usePeopleFiltering && aPersonType != bPersonType -> peopleNotificationIdentifier.compareTo(aPersonType, bPersonType) // Upsort current media notification. @@ -106,11 +109,6 @@ open class NotificationRankingManager @Inject constructor( } } - private fun isImportantMedia(entry: NotificationEntry): Boolean { - val importance = entry.ranking.importance - return entry.key == mediaManager.mediaNotificationKey && importance > IMPORTANCE_MIN - } - fun updateRanking( newRankingMap: RankingMap?, entries: Collection<NotificationEntry>, @@ -153,15 +151,12 @@ open class NotificationRankingManager @Inject constructor( @PriorityBucket private fun getBucketForEntry(entry: NotificationEntry): Int { val isHeadsUp = entry.isRowHeadsUp - val isMedia = isImportantMedia(entry) + val isMedia = entry.isImportantMedia() val isSystemMax = entry.isSystemMax() return when { - entry.sbn.notification.isForegroundService && entry.sbn.notification.isColorized -> - BUCKET_FOREGROUND_SERVICE - usePeopleFiltering && entry.getPeopleNotificationType() != TYPE_NON_PERSON -> - BUCKET_PEOPLE - isHeadsUp || isMedia || isSystemMax || entry.isHighPriority() -> - BUCKET_ALERTING + entry.isColorizedForegroundService() -> BUCKET_FOREGROUND_SERVICE + usePeopleFiltering && entry.isConversation() -> BUCKET_PEOPLE + isHeadsUp || isMedia || isSystemMax || entry.isHighPriority() -> BUCKET_ALERTING else -> BUCKET_SILENT } } @@ -190,6 +185,11 @@ open class NotificationRankingManager @Inject constructor( } } + private fun NotificationEntry.isImportantMedia() = + key == mediaManager.mediaNotificationKey && ranking.importance > IMPORTANCE_MIN + + private fun NotificationEntry.isConversation() = getPeopleNotificationType() != TYPE_NON_PERSON + private fun NotificationEntry.getPeopleNotificationType() = peopleNotificationIdentifier.getPeopleNotificationType(sbn, ranking) @@ -198,10 +198,12 @@ open class NotificationRankingManager @Inject constructor( } // Convenience functions -private fun NotificationEntry.isSystemMax(): Boolean { - return importance >= IMPORTANCE_HIGH && sbn.isSystemNotification() -} +private fun NotificationEntry.isSystemMax() = + importance >= IMPORTANCE_HIGH && sbn.isSystemNotification() + +private fun StatusBarNotification.isSystemNotification() = + "android" == packageName || "com.android.systemui" == packageName -private fun StatusBarNotification.isSystemNotification(): Boolean { - return "android" == packageName || "com.android.systemui" == packageName +private fun NotificationEntry.isColorizedForegroundService() = sbn.notification.run { + isForegroundService && isColorized && importance > IMPORTANCE_MIN } 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/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index e9849ec84987..9925909c3e16 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -64,6 +64,7 @@ import com.android.systemui.statusbar.policy.SmartReplyView; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; /** * A frame layout containing the actual payload of the notification, including the contracted, @@ -518,9 +519,12 @@ public class NotificationContentView extends FrameLayout { protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); updateVisibility(); - if (visibility != VISIBLE) { + if (visibility != VISIBLE && !mOnContentViewInactiveListeners.isEmpty()) { // View is no longer visible so all content views are inactive. - for (Runnable r : mOnContentViewInactiveListeners.values()) { + // Clone list as runnables may modify the list of listeners + ArrayList<Runnable> listeners = new ArrayList<>( + mOnContentViewInactiveListeners.values()); + for (Runnable r : listeners) { r.run(); } mOnContentViewInactiveListeners.clear(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java index 66c07bd94e59..1dc828bfb0b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java @@ -192,9 +192,9 @@ public class PartialConversationInfo extends LinearLayout implements private void bindName() { TextView name = findViewById(R.id.name); Bundle extras = mSbn.getNotification().extras; - String nameString = extras.getString(Notification.EXTRA_CONVERSATION_TITLE); + CharSequence nameString = extras.getCharSequence(Notification.EXTRA_CONVERSATION_TITLE, ""); if (TextUtils.isEmpty(nameString)) { - nameString = extras.getString(Notification.EXTRA_TITLE); + nameString = extras.getCharSequence(Notification.EXTRA_TITLE, ""); } name.setText(nameString); } 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/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 3db4b6f7ffbb..e33cc6027c4f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -5983,6 +5983,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd // ANIMATION_TYPE_ADD new AnimationFilter() + .animateAlpha() .animateHeight() .animateTopInset() .animateY() @@ -5991,6 +5992,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd // ANIMATION_TYPE_REMOVE new AnimationFilter() + .animateAlpha() .animateHeight() .animateTopInset() .animateY() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index a065b74bda99..2a4475bbe6b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -46,6 +46,8 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; +import com.android.systemui.media.MediaData; +import com.android.systemui.media.MediaDataManager; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.RotationLockTile; import com.android.systemui.screenrecord.RecordingController; @@ -80,14 +82,14 @@ import javax.inject.Inject; */ public class PhoneStatusBarPolicy implements BluetoothController.Callback, - CommandQueue.Callbacks, - RotationLockControllerCallback, - Listener, - ZenModeController.Callback, - DeviceProvisionedListener, - KeyguardStateController.Callback, - LocationController.LocationChangeCallback, - RecordingController.RecordingStateChangeCallback { + CommandQueue.Callbacks, + RotationLockControllerCallback, + Listener, + ZenModeController.Callback, + DeviceProvisionedListener, + KeyguardStateController.Callback, + LocationController.LocationChangeCallback, + RecordingController.RecordingStateChangeCallback, MediaDataManager.Listener { private static final String TAG = "PhoneStatusBarPolicy"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -108,6 +110,7 @@ public class PhoneStatusBarPolicy private final String mSlotLocation; private final String mSlotSensorsOff; private final String mSlotScreenRecord; + private final String mSlotMedia; private final int mDisplayId; private final SharedPreferences mSharedPreferences; private final DateFormatUtil mDateFormatUtil; @@ -135,6 +138,7 @@ public class PhoneStatusBarPolicy private final SensorPrivacyController mSensorPrivacyController; private final RecordingController mRecordingController; private final RingerModeTracker mRingerModeTracker; + private final MediaDataManager mMediaDataManager; private boolean mZenVisible; private boolean mVolumeVisible; @@ -159,6 +163,7 @@ public class PhoneStatusBarPolicy SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager, AlarmManager alarmManager, UserManager userManager, RecordingController recordingController, + MediaDataManager mediaDataManager, @Nullable TelecomManager telecomManager, @DisplayId int displayId, @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil, RingerModeTracker ringerModeTracker) { @@ -185,6 +190,7 @@ public class PhoneStatusBarPolicy mUiBgExecutor = uiBgExecutor; mTelecomManager = telecomManager; mRingerModeTracker = ringerModeTracker; + mMediaDataManager = mediaDataManager; mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast); mSlotHotspot = resources.getString(com.android.internal.R.string.status_bar_hotspot); @@ -202,6 +208,7 @@ public class PhoneStatusBarPolicy mSlotSensorsOff = resources.getString(com.android.internal.R.string.status_bar_sensors_off); mSlotScreenRecord = resources.getString( com.android.internal.R.string.status_bar_screen_record); + mSlotMedia = resources.getString(com.android.internal.R.string.status_bar_media); mDisplayId = displayId; mSharedPreferences = sharedPreferences; @@ -280,6 +287,11 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotSensorsOff, mSensorPrivacyController.isSensorPrivacyEnabled()); + // play/pause icon when media is active + mIconController.setIcon(mSlotMedia, R.drawable.stat_sys_media, + mResources.getString(R.string.accessibility_media_active)); + mIconController.setIconVisibility(mSlotMedia, mMediaDataManager.hasActiveMedia()); + // screen record mIconController.setIcon(mSlotScreenRecord, R.drawable.stat_sys_screen_record, null); mIconController.setIconVisibility(mSlotScreenRecord, false); @@ -296,6 +308,7 @@ public class PhoneStatusBarPolicy mSensorPrivacyController.addCallback(mSensorPrivacyListener); mLocationController.addCallback(this); mRecordingController.addCallback(this); + mMediaDataManager.addListener(this); mCommandQueue.addCallback(this); } @@ -700,4 +713,18 @@ public class PhoneStatusBarPolicy if (DEBUG) Log.d(TAG, "screenrecord: hiding icon"); mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false)); } + + @Override + public void onMediaDataLoaded(String key, MediaData data) { + updateMediaIcon(); + } + + @Override + public void onMediaDataRemoved(String key) { + updateMediaIcon(); + } + + private void updateMediaIcon() { + mIconController.setIconVisibility(mSlotMedia, mMediaDataManager.hasActiveMedia()); + } } 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/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt new file mode 100644 index 000000000000..9aee11e4924f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt @@ -0,0 +1,100 @@ +/* + * 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.view.View.GONE +import android.view.View.VISIBLE +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq +import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.SysuiStatusBarStateController +import com.android.systemui.statusbar.notification.stack.MediaHeaderView +import com.android.systemui.statusbar.phone.KeyguardBypassController +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class KeyguardMediaControllerTest : SysuiTestCase() { + + @Mock + private lateinit var mediaHost: MediaHost + @Mock + private lateinit var bypassController: KeyguardBypassController + @Mock + private lateinit var statusBarStateController: SysuiStatusBarStateController + @Mock + private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager + @Mock + private lateinit var mediaHeaderView: MediaHeaderView + @Captor + private lateinit var visibilityListener: ArgumentCaptor<((Boolean) -> Unit)> + @JvmField @Rule + val mockito = MockitoJUnit.rule() + private lateinit var keyguardMediaController: KeyguardMediaController + + @Before + fun setup() { + keyguardMediaController = KeyguardMediaController(mediaHost, bypassController, + statusBarStateController, notificationLockscreenUserManager) + } + + @Test + fun testAttach_hiddenWhenHostIsHidden() { + `when`(mediaHost.visible).thenReturn(false) + triggerVisibilityListener() + + verify(mediaHeaderView).visibility = eq(GONE) + } + @Test + fun testAttach_visibleOnKeyguard() { + `when`(mediaHost.visible).thenReturn(true) + `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()) + .thenReturn(true) + triggerVisibilityListener() + + verify(mediaHeaderView).visibility = eq(VISIBLE) + } + @Test + fun testAttach_hiddenOnKeyguard_whenNotificationsAreHidden() { + `when`(mediaHost.visible).thenReturn(true) + `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()) + .thenReturn(false) + triggerVisibilityListener() + + verify(mediaHeaderView).visibility = eq(GONE) + } + + private fun triggerVisibilityListener() { + keyguardMediaController.attach(mediaHeaderView) + verify(mediaHost).visibleChangedListener = visibilityListener.capture() + visibilityListener.value.invoke(true) + } +}
\ No newline at end of file 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..e8fb41a18ce9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -0,0 +1,210 @@ +/* + * 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.graphics.drawable.GradientDrawable +import android.graphics.drawable.RippleDrawable +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, 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) + val seamlessBackground = mock(RippleDrawable::class.java) + seamless.setBackground(seamlessBackground) + whenever(seamlessBackground.getDrawable(0)).thenReturn(mock(GradientDrawable::class.java)) + 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, MediaDeviceData(null, DEVICE_NAME)) + 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, + MediaDeviceData(null, DEVICE_NAME)) + 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, + MediaDeviceData(null, DEVICE_NAME)) + player.bind(state) + assertThat(background.getBackgroundTintList()).isEqualTo(ColorStateList.valueOf(BG_COLOR)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java new file mode 100644 index 000000000000..64a180f8aaab --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java @@ -0,0 +1,161 @@ +/* + * 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 static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.graphics.Color; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; + +import java.util.ArrayList; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class MediaDataCombineLatestTest extends SysuiTestCase { + + private static final String KEY = "TEST_KEY"; + private static final String APP = "APP"; + private static final String PACKAGE = "PKG"; + private static final int BG_COLOR = Color.RED; + private static final String ARTIST = "ARTIST"; + private static final String TITLE = "TITLE"; + private static final String DEVICE_NAME = "DEVICE_NAME"; + + private MediaDataCombineLatest mManager; + + @Mock private MediaDataManager mDataSource; + @Mock private MediaDeviceManager mDeviceSource; + @Mock private MediaDataManager.Listener mListener; + + private MediaDataManager.Listener mDataListener; + private MediaDeviceManager.Listener mDeviceListener; + + private MediaData mMediaData; + private MediaDeviceData mDeviceData; + + @Before + public void setUp() { + mDataSource = mock(MediaDataManager.class); + mDeviceSource = mock(MediaDeviceManager.class); + mListener = mock(MediaDataManager.Listener.class); + + mManager = new MediaDataCombineLatest(mDataSource, mDeviceSource); + + mDataListener = captureDataListener(); + mDeviceListener = captureDeviceListener(); + + mManager.addListener(mListener); + + mMediaData = new MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, + new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null); + mDeviceData = new MediaDeviceData(null, DEVICE_NAME); + } + + @Test + public void eventNotEmittedWithoutDevice() { + // WHEN data source emits an event without device data + mDataListener.onMediaDataLoaded(KEY, mMediaData); + // THEN an event isn't emitted + verify(mListener, never()).onMediaDataLoaded(eq(KEY), any()); + } + + @Test + public void eventNotEmittedWithoutMedia() { + // WHEN device source emits an event without media data + mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + // THEN an event isn't emitted + verify(mListener, never()).onMediaDataLoaded(eq(KEY), any()); + } + + @Test + public void emitEventAfterDeviceFirst() { + // GIVEN that a device event has already been received + mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + // WHEN media event is received + mDataListener.onMediaDataLoaded(KEY, mMediaData); + // THEN the listener receives a combined event + ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); + verify(mListener).onMediaDataLoaded(eq(KEY), captor.capture()); + assertThat(captor.getValue().getDevice()).isNotNull(); + } + + @Test + public void emitEventAfterMediaFirst() { + // GIVEN that media event has already been received + mDataListener.onMediaDataLoaded(KEY, mMediaData); + // WHEN device event is received + mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + // THEN the listener receives a combined event + ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); + verify(mListener).onMediaDataLoaded(eq(KEY), captor.capture()); + assertThat(captor.getValue().getDevice()).isNotNull(); + } + + @Test + public void mediaDataRemoved() { + // WHEN media data is removed without first receiving device or data + mDataListener.onMediaDataRemoved(KEY); + // THEN a removed event isn't emitted + verify(mListener, never()).onMediaDataRemoved(eq(KEY)); + } + + @Test + public void mediaDataRemovedAfterMediaEvent() { + mDataListener.onMediaDataLoaded(KEY, mMediaData); + mDataListener.onMediaDataRemoved(KEY); + verify(mListener).onMediaDataRemoved(eq(KEY)); + } + + @Test + public void mediaDataRemovedAfterDeviceEvent() { + mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData); + mDataListener.onMediaDataRemoved(KEY); + verify(mListener).onMediaDataRemoved(eq(KEY)); + } + + private MediaDataManager.Listener captureDataListener() { + ArgumentCaptor<MediaDataManager.Listener> captor = ArgumentCaptor.forClass( + MediaDataManager.Listener.class); + verify(mDataSource).addListener(captor.capture()); + return captor.getValue(); + } + + private MediaDeviceManager.Listener captureDeviceListener() { + ArgumentCaptor<MediaDeviceManager.Listener> captor = ArgumentCaptor.forClass( + MediaDeviceManager.Listener.class); + verify(mDeviceSource).addListener(captor.capture()); + return captor.getValue(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt new file mode 100644 index 000000000000..ac6b5f6bca66 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt @@ -0,0 +1,195 @@ +/* + * 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.app.Notification +import android.media.MediaMetadata +import android.media.session.MediaSession +import android.media.session.PlaybackState +import android.os.Process +import android.service.notification.StatusBarNotification +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest + +import com.android.settingslib.media.LocalMediaManager +import com.android.settingslib.media.MediaDevice +import com.android.systemui.SysuiTestCase +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.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.any +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever + +private const val KEY = "TEST_KEY" +private const val PACKAGE = "PKG" +private const val SESSION_KEY = "SESSION_KEY" +private const val SESSION_ARTIST = "SESSION_ARTIST" +private const val SESSION_TITLE = "SESSION_TITLE" +private const val DEVICE_NAME = "DEVICE_NAME" + +private fun <T> eq(value: T): T = Mockito.eq(value) ?: value + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +public class MediaDeviceManagerTest : SysuiTestCase() { + + private lateinit var manager: MediaDeviceManager + + @Mock private lateinit var lmmFactory: LocalMediaManagerFactory + @Mock private lateinit var lmm: LocalMediaManager + @Mock private lateinit var featureFlag: MediaFeatureFlag + private lateinit var fakeExecutor: FakeExecutor + + @Mock private lateinit var device: MediaDevice + private lateinit var session: MediaSession + private lateinit var metadataBuilder: MediaMetadata.Builder + private lateinit var playbackBuilder: PlaybackState.Builder + private lateinit var notifBuilder: Notification.Builder + private lateinit var sbn: StatusBarNotification + + @Before + fun setup() { + lmmFactory = mock(LocalMediaManagerFactory::class.java) + lmm = mock(LocalMediaManager::class.java) + device = mock(MediaDevice::class.java) + whenever(device.name).thenReturn(DEVICE_NAME) + whenever(lmmFactory.create(PACKAGE)).thenReturn(lmm) + whenever(lmm.getCurrentConnectedDevice()).thenReturn(device) + featureFlag = mock(MediaFeatureFlag::class.java) + whenever(featureFlag.enabled).thenReturn(true) + + fakeExecutor = FakeExecutor(FakeSystemClock()) + + manager = MediaDeviceManager(context, lmmFactory, featureFlag, fakeExecutor) + + // Create a media sesssion and notification for testing. + metadataBuilder = MediaMetadata.Builder().apply { + putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST) + putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE) + } + 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) + notifBuilder = Notification.Builder(context, "NONE").apply { + setContentTitle(SESSION_TITLE) + setContentText(SESSION_ARTIST) + setSmallIcon(android.R.drawable.ic_media_pause) + setStyle(Notification.MediaStyle().setMediaSession(session.getSessionToken())) + } + sbn = StatusBarNotification(PACKAGE, PACKAGE, 0, "TAG", Process.myUid(), 0, 0, + notifBuilder.build(), Process.myUserHandle(), 0) + } + + @After + fun tearDown() { + session.release() + } + + @Test + fun removeUnknown() { + manager.onNotificationRemoved("unknown") + } + + @Test + fun addNotification() { + manager.onNotificationAdded(KEY, sbn) + verify(lmmFactory).create(PACKAGE) + } + + @Test + fun featureDisabled() { + whenever(featureFlag.enabled).thenReturn(false) + manager.onNotificationAdded(KEY, sbn) + verify(lmmFactory, never()).create(PACKAGE) + } + + @Test + fun addAndRemoveNotification() { + manager.onNotificationAdded(KEY, sbn) + manager.onNotificationRemoved(KEY) + verify(lmm).unregisterCallback(any()) + } + + @Test + fun deviceListUpdate() { + val listener = mock(MediaDeviceManager.Listener::class.java) + manager.addListener(listener) + manager.onNotificationAdded(KEY, sbn) + val deviceCallback = captureCallback() + // WHEN the device list changes + deviceCallback.onDeviceListUpdate(mutableListOf(device)) + assertThat(fakeExecutor.runAllReady()).isEqualTo(1) + // THEN the update is dispatched to the listener + val captor = ArgumentCaptor.forClass(MediaDeviceData::class.java) + verify(listener).onMediaDeviceChanged(eq(KEY), captor.capture()) + val data = captor.getValue() + assertThat(data.name).isEqualTo(DEVICE_NAME) + } + + @Test + fun selectedDeviceStateChanged() { + val listener = mock(MediaDeviceManager.Listener::class.java) + manager.addListener(listener) + manager.onNotificationAdded(KEY, sbn) + val deviceCallback = captureCallback() + // WHEN the selected device changes state + deviceCallback.onSelectedDeviceStateChanged(device, 1) + assertThat(fakeExecutor.runAllReady()).isEqualTo(1) + // THEN the update is dispatched to the listener + val captor = ArgumentCaptor.forClass(MediaDeviceData::class.java) + verify(listener).onMediaDeviceChanged(eq(KEY), captor.capture()) + val data = captor.getValue() + assertThat(data.name).isEqualTo(DEVICE_NAME) + } + + @Test + fun listenerReceivesKeyRemoved() { + manager.onNotificationAdded(KEY, sbn) + val listener = mock(MediaDeviceManager.Listener::class.java) + manager.addListener(listener) + // WHEN the notification is removed + manager.onNotificationRemoved(KEY) + // THEN the listener receives key removed event + verify(listener).onKeyRemoved(eq(KEY)) + } + + fun captureCallback(): LocalMediaManager.DeviceCallback { + val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java) + verify(lmm).registerCallback(captor.capture()) + return captor.getValue() + } +} 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/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt index c46ac47e660c..e889dad0f610 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.ActivityLaunchAnimator import com.android.systemui.statusbar.phone.BiometricUnlockController +import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.NotificationShadeWindowController import com.android.systemui.statusbar.policy.KeyguardStateController import org.junit.Before @@ -67,6 +68,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { @Mock private lateinit var shadeAnimation: NotificationShadeDepthController.DepthAnimation @Mock private lateinit var globalActionsSpring: NotificationShadeDepthController.DepthAnimation @Mock private lateinit var brightnessSpring: NotificationShadeDepthController.DepthAnimation + @Mock private lateinit var dozeParameters: DozeParameters @JvmField @Rule val mockitoRule = MockitoJUnit.rule() private lateinit var statusBarStateListener: StatusBarStateController.StateListener @@ -87,7 +89,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { notificationShadeDepthController = NotificationShadeDepthController( statusBarStateController, blurUtils, biometricUnlockController, keyguardStateController, choreographer, wallpaperManager, - notificationShadeWindowController, dumpManager) + notificationShadeWindowController, dozeParameters, dumpManager) notificationShadeDepthController.shadeSpring = shadeSpring notificationShadeDepthController.shadeAnimation = shadeAnimation notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt index 9f47f4a71ca9..1c47131e2e86 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt @@ -38,9 +38,11 @@ import com.android.systemui.statusbar.notification.people.PeopleNotificationIden import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING +import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT import com.android.systemui.statusbar.phone.NotificationGroupManager import com.android.systemui.statusbar.policy.HeadsUpManager +import com.google.common.truth.Truth.assertThat import dagger.Lazy import junit.framework.Assert.assertEquals import org.junit.Before @@ -353,6 +355,58 @@ class NotificationRankingManagerTest : SysuiTestCase() { assertEquals(false, e.hasFinishedInitialization()) } + @Test + fun testSort_colorizedForegroundService() { + whenever(sectionsManager.isFilteringEnabled()).thenReturn(true) + + val a = NotificationEntryBuilder() + .setImportance(IMPORTANCE_HIGH) + .setPkg("pkg") + .setOpPkg("pkg") + .setTag("tag") + .setNotification( + Notification.Builder(mContext, "test") + .build()) + .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build() + + val b = NotificationEntryBuilder() + .setImportance(IMPORTANCE_DEFAULT) // high priority + .setPkg("pkg2") + .setOpPkg("pkg2") + .setTag("tag") + .setNotification(mock(Notification::class.java).also { notif -> + whenever(notif.isForegroundService).thenReturn(true) + whenever(notif.isColorized).thenReturn(true) + }) + .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build() + + val cN = Notification.Builder(mContext, "test") + .setStyle(Notification.MessagingStyle("")) + .build() + val c = NotificationEntryBuilder() + .setImportance(IMPORTANCE_HIGH) + .setPkg("pkg") + .setOpPkg("pkg") + .setTag("tag") + .setNotification(cN) + .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) + .setUser(mContext.user) + .setOverrideGroupKey("") + .build() + whenever(personNotificationIdentifier.getPeopleNotificationType(a.sbn, a.ranking)) + .thenReturn(TYPE_IMPORTANT_PERSON) + + assertThat(rankingManager.updateRanking(null, listOf(a, b, c), "test")) + .containsExactly(b, c, a) + assertThat(b.bucket).isEqualTo(BUCKET_FOREGROUND_SERVICE) + } + internal class TestableNotificationRankingManager( mediaManager: Lazy<NotificationMediaManager>, groupManager: NotificationGroupManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java index f21b1a649b3e..545b59a4556a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java @@ -47,6 +47,7 @@ import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.text.SpannableString; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; @@ -151,8 +152,11 @@ public class PartialConversationInfoTest extends SysuiTestCase { NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_LOW); mDefaultNotificationChannelSet.add(mDefaultNotificationChannel); + Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) + .setContentTitle(new SpannableString("title")) + .build(); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, - new Notification(), UserHandle.CURRENT, null, 0); + n, UserHandle.CURRENT, null, 0); mEntry = new NotificationEntryBuilder().setSbn(mSbn).build(); } @@ -176,6 +180,23 @@ public class PartialConversationInfoTest extends SysuiTestCase { } @Test + public void testBindNotification_SetsName() { + mInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mChannelEditorDialogController, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + true, + false); + final TextView textView = mInfo.findViewById(R.id.name); + assertTrue(textView.getText().toString().contains("title")); + } + + @Test public void testBindNotification_groupSetsPackageIcon() { mEntry.getSbn().getNotification().extras.putBoolean(EXTRA_IS_GROUP_CONVERSATION, true); final Drawable iconDrawable = mock(Drawable.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.java new file mode 100644 index 000000000000..a14d57556360 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.AlarmManager; +import android.app.IActivityManager; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.os.UserManager; +import android.telecom.TelecomManager; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.media.MediaDataManager; +import com.android.systemui.screenrecord.RecordingController; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.policy.BluetoothController; +import com.android.systemui.statusbar.policy.CastController; +import com.android.systemui.statusbar.policy.DataSaverController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HotspotController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.LocationController; +import com.android.systemui.statusbar.policy.NextAlarmController; +import com.android.systemui.statusbar.policy.RotationLockController; +import com.android.systemui.statusbar.policy.SensorPrivacyController; +import com.android.systemui.statusbar.policy.UserInfoController; +import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.RingerModeLiveData; +import com.android.systemui.util.RingerModeTracker; +import com.android.systemui.util.time.DateFormatUtil; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.concurrent.Executor; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class PhoneStatusBarPolicyTest extends SysuiTestCase { + + private static final int DISPLAY_ID = 0; + @Mock + private StatusBarIconController mIconController; + @Mock + private CommandQueue mCommandQueue; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private Executor mBackgroundExecutor; + @Mock + private CastController mCastController; + @Mock + private HotspotController mHotSpotController; + @Mock + private BluetoothController mBluetoothController; + @Mock + private NextAlarmController mNextAlarmController; + @Mock + private UserInfoController mUserInfoController; + @Mock + private RotationLockController mRotationLockController; + @Mock + private DataSaverController mDataSaverController; + @Mock + private ZenModeController mZenModeController; + @Mock + private DeviceProvisionedController mDeviceProvisionerController; + @Mock + private KeyguardStateController mKeyguardStateController; + @Mock + private LocationController mLocationController; + @Mock + private SensorPrivacyController mSensorPrivacyController; + @Mock + private IActivityManager mIActivityManager; + @Mock + private AlarmManager mAlarmManager; + @Mock + private UserManager mUserManager; + @Mock + private RecordingController mRecordingController; + @Mock + private MediaDataManager mMediaDataManager; + @Mock + private TelecomManager mTelecomManager; + @Mock + private SharedPreferences mSharedPreferences; + @Mock + private DateFormatUtil mDateFormatUtil; + @Mock + private RingerModeTracker mRingerModeTracker; + @Mock + private RingerModeLiveData mRingerModeLiveData; + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + private Resources mResources; + private PhoneStatusBarPolicy mPhoneStatusBarPolicy; + + @Before + public void setup() { + mResources = spy(getContext().getResources()); + mPhoneStatusBarPolicy = new PhoneStatusBarPolicy(mIconController, mCommandQueue, + mBroadcastDispatcher, mBackgroundExecutor, mResources, mCastController, + mHotSpotController, mBluetoothController, mNextAlarmController, mUserInfoController, + mRotationLockController, mDataSaverController, mZenModeController, + mDeviceProvisionerController, mKeyguardStateController, mLocationController, + mSensorPrivacyController, mIActivityManager, mAlarmManager, mUserManager, + mRecordingController, mMediaDataManager, mTelecomManager, DISPLAY_ID, + mSharedPreferences, mDateFormatUtil, mRingerModeTracker); + when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData); + when(mRingerModeTracker.getRingerModeInternal()).thenReturn(mRingerModeLiveData); + clearInvocations(mIconController); + } + + @Test + public void testInit_registerMediaCallback() { + mPhoneStatusBarPolicy.init(); + verify(mMediaDataManager).addListener(eq(mPhoneStatusBarPolicy)); + } + + @Test + public void testOnMediaDataLoaded_updatesIcon_hasMedia() { + String mediaSlot = mResources.getString(com.android.internal.R.string.status_bar_media); + when(mMediaDataManager.hasActiveMedia()).thenReturn(true); + mPhoneStatusBarPolicy.onMediaDataLoaded(null, null); + verify(mMediaDataManager).hasActiveMedia(); + verify(mIconController).setIconVisibility(eq(mediaSlot), eq(true)); + } + + @Test + public void testOnMediaDataRemoved_updatesIcon_noMedia() { + String mediaSlot = mResources.getString(com.android.internal.R.string.status_bar_media); + mPhoneStatusBarPolicy.onMediaDataRemoved(null); + verify(mMediaDataManager).hasActiveMedia(); + verify(mIconController).setIconVisibility(eq(mediaSlot), eq(false)); + } +} 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/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index e2a0c29dbf01..ee0f71b9e982 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -42,8 +42,8 @@ import static android.os.storage.OnObbStateChangeListener.ERROR_NOT_MOUNTED; import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIED; import static android.os.storage.OnObbStateChangeListener.MOUNTED; import static android.os.storage.OnObbStateChangeListener.UNMOUNTED; +import static android.os.storage.StorageManager.PROP_FORCED_SCOPED_STORAGE_WHITELIST; import static android.os.storage.StorageManager.PROP_FUSE; -import static android.os.storage.StorageManager.PROP_LEGACY_OP_STICKY; import static android.os.storage.StorageManager.PROP_SETTINGS_FUSE; import static com.android.internal.util.XmlUtils.readIntAttribute; @@ -914,7 +914,6 @@ class StorageManagerService extends IStorageManager.Stub refreshIsolatedStorageSettings(); } }); - updateLegacyStorageOpSticky(); // For now, simply clone property when it changes DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, mContext.getMainExecutor(), (properties) -> { @@ -1836,13 +1835,6 @@ class StorageManagerService extends IStorageManager.Stub } } - private void updateLegacyStorageOpSticky() { - final boolean propertyValue = DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, - "legacy_storage_op_sticky", true); - SystemProperties.set(PROP_LEGACY_OP_STICKY, propertyValue ? "true" : "false"); - } - private void start() { connectStoraged(); connectVold(); @@ -4442,6 +4434,9 @@ class StorageManagerService extends IStorageManager.Stub pw.println("Isolated storage, remote feature flag: " + Settings.Global.getInt(cr, Settings.Global.ISOLATED_STORAGE_REMOTE, 0)); pw.println("Isolated storage, resolved: " + StorageManager.hasIsolatedStorage()); + pw.println("Forced scoped storage app list: " + + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, + PROP_FORCED_SCOPED_STORAGE_WHITELIST)); } synchronized (mObbMounts) { 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/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java index 5d334c22f2db..edc8f15a9a03 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java @@ -84,11 +84,8 @@ public abstract class AuthenticationClient extends ClientMonitor { @Override public void binderDied() { - super.binderDied(); - // When the binder dies, we should stop the client. This probably belongs in - // ClientMonitor's binderDied(), but testing all the cases would be tricky. - // AuthenticationClient is the most user-visible case. - stop(false /* initiatedByClient */); + final boolean clearListener = !isBiometricPrompt(); + binderDiedInternal(clearListener); } @Override diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 7e28e94a17bb..4ddfe1b6e2d2 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -113,6 +113,7 @@ public class BiometricService extends SystemService { private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 11; private static final int MSG_ON_DEVICE_CREDENTIAL_PRESSED = 12; private static final int MSG_ON_SYSTEM_EVENT = 13; + private static final int MSG_CLIENT_DIED = 14; /** * Authentication either just called and we have not transitioned to the CALLED state, or @@ -151,8 +152,13 @@ public class BiometricService extends SystemService { * Device credential in AuthController is showing */ static final int STATE_SHOWING_DEVICE_CREDENTIAL = 8; + /** + * The client binder died, and sensors were authenticating at the time. Cancel has been + * requested and we're waiting for the HAL(s) to send ERROR_CANCELED. + */ + static final int STATE_CLIENT_DIED_CANCELLING = 9; - final class AuthSession { + final class AuthSession implements IBinder.DeathRecipient { // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from // <Biometric>Services before we can start authenticating. Pairs that have been returned // are moved to mModalitiesMatched. @@ -211,7 +217,14 @@ public class BiometricService extends SystemService { mCallingUserId = callingUserId; mModality = modality; mRequireConfirmation = requireConfirmation; + Slog.d(TAG, "New AuthSession, mSysUiSessionId: " + mSysUiSessionId); + + try { + mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to link to death"); + } } boolean isCrypto() { @@ -231,6 +244,12 @@ public class BiometricService extends SystemService { boolean isAllowDeviceCredential() { return Utils.isCredentialRequested(mBundle); } + + @Override + public void binderDied() { + Slog.e(TAG, "Binder died, sysUiSessionId: " + mSysUiSessionId); + mHandler.obtainMessage(MSG_CLIENT_DIED).sendToTarget(); + } } private final Injector mInjector; @@ -370,6 +389,11 @@ public class BiometricService extends SystemService { break; } + case MSG_CLIENT_DIED: { + handleClientDied(); + break; + } + default: Slog.e(TAG, "Unknown message: " + msg); break; @@ -1391,6 +1415,7 @@ public class BiometricService extends SystemService { } private void handleOnError(int cookie, int modality, int error, int vendorCode) { + Slog.d(TAG, "handleOnError: " + error + " cookie: " + cookie); // Errors can either be from the current auth session or the pending auth session. // The pending auth session may receive errors such as ERROR_LOCKOUT before @@ -1431,6 +1456,9 @@ public class BiometricService extends SystemService { } else if (mCurrentAuthSession.mState == STATE_SHOWING_DEVICE_CREDENTIAL) { Slog.d(TAG, "Biometric canceled, ignoring from state: " + mCurrentAuthSession.mState); + } else if (mCurrentAuthSession.mState == STATE_CLIENT_DIED_CANCELLING) { + mStatusBarService.hideAuthenticationDialog(); + mCurrentAuthSession = null; } else { Slog.e(TAG, "Impossible session error state: " + mCurrentAuthSession.mState); @@ -1622,6 +1650,36 @@ public class BiometricService extends SystemService { } } + private void handleClientDied() { + if (mCurrentAuthSession == null) { + Slog.e(TAG, "Auth session null"); + return; + } + + Slog.e(TAG, "SysUiSessionId: " + mCurrentAuthSession.mSysUiSessionId + + " State: " + mCurrentAuthSession.mState); + + try { + // Check if any sensors are authenticating. If so, need to cancel them. When + // ERROR_CANCELED is received from the HAL, we hide the dialog and cleanup the session. + if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) { + mCurrentAuthSession.mState = STATE_CLIENT_DIED_CANCELLING; + cancelInternal(mCurrentAuthSession.mToken, + mCurrentAuthSession.mOpPackageName, + mCurrentAuthSession.mCallingUid, + mCurrentAuthSession.mCallingPid, + mCurrentAuthSession.mCallingUserId, + false /* fromClient */); + } else { + // If the sensors are not authenticating, set the auth session to null. + mStatusBarService.hideAuthenticationDialog(); + mCurrentAuthSession = null; + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception: " + e); + } + } + /** * Invoked when each service has notified that its client is ready to be started. When * all biometrics are ready, this invokes the SystemUI dialog through StatusBar. @@ -1822,11 +1880,11 @@ public class BiometricService extends SystemService { void cancelInternal(IBinder token, String opPackageName, int callingUid, int callingPid, int callingUserId, boolean fromClient) { - if (mCurrentAuthSession == null) { Slog.w(TAG, "Skipping cancelInternal"); return; - } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED) { + } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED + && mCurrentAuthSession.mState != STATE_CLIENT_DIED_CANCELLING) { Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState); return; } diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java index 942e0501d88d..b02969524221 100644 --- a/services/core/java/com/android/server/biometrics/ClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java @@ -233,11 +233,17 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D @Override public void binderDied() { + binderDiedInternal(true /* clearListener */); + } + + void binderDiedInternal(boolean clearListener) { // If the current client dies we should cancel the current operation. Slog.e(getLogTag(), "Binder died, cancelling client"); stop(false /* initiatedByClient */); mToken = null; - mListener = null; + if (clearListener) { + mListener = null; + } } @Override diff --git a/services/core/java/com/android/server/compat/TEST_MAPPING b/services/core/java/com/android/server/compat/TEST_MAPPING index 0c30c790c5dd..bc1c7287d04a 100644 --- a/services/core/java/com/android/server/compat/TEST_MAPPING +++ b/services/core/java/com/android/server/compat/TEST_MAPPING @@ -15,7 +15,7 @@ }, // CTS tests { - "name": "CtsAppCompatHostTestCases#" + "name": "CtsAppCompatHostTestCases" } ] }
\ No newline at end of file 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/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index d7bd794743d4..c65800a17f82 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -808,14 +808,11 @@ class MediaRouter2ServiceImpl { userRecord.mHandler, manager)); for (RouterRecord routerRecord : userRecord.mRouterRecords) { - // TODO: Do not use notifyPreferredFeaturesChangedToManagers since it updates all - // managers. Instead, Notify only to the manager that is currently being registered. - // TODO: UserRecord <-> routerRecord, why do they reference each other? // How about removing mUserRecord from routerRecord? routerRecord.mUserRecord.mHandler.sendMessage( - obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManagers, - routerRecord.mUserRecord.mHandler, routerRecord)); + obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManager, + routerRecord.mUserRecord.mHandler, routerRecord, manager)); } } @@ -1928,6 +1925,17 @@ class MediaRouter2ServiceImpl { } } + private void notifyPreferredFeaturesChangedToManager(@NonNull RouterRecord routerRecord, + @NonNull IMediaRouter2Manager manager) { + try { + manager.notifyPreferredFeaturesChanged(routerRecord.mPackageName, + routerRecord.mDiscoveryPreference.getPreferredFeatures()); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify preferred features changed." + + " Manager probably died.", ex); + } + } + private void notifyPreferredFeaturesChangedToManagers(@NonNull RouterRecord routerRecord) { MediaRouter2ServiceImpl service = mServiceRef.get(); if (service == null) { 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/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 9de34a92cdf7..a5b1bf98cdb7 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -5595,10 +5595,7 @@ public final class Settings { userId); } else if (packageSetting.sharedUser == null && !isUpgradeToR) { Slog.w(TAG, "Missing permission state for package: " + packageName); - generateFallbackPermissionsStateLpr( - packageSetting.pkg.getRequestedPermissions(), - packageSetting.pkg.getTargetSdkVersion(), - packageSetting.getPermissionsState(), userId); + packageSetting.getPermissionsState().setMissing(true, userId); } } @@ -5616,22 +5613,7 @@ public final class Settings { userId); } else if (!isUpgradeToR) { Slog.w(TAG, "Missing permission state for shared user: " + sharedUserName); - ArraySet<String> requestedPermissions = new ArraySet<>(); - int targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; - int sharedUserPackagesSize = sharedUserSetting.packages.size(); - for (int packagesI = 0; packagesI < sharedUserPackagesSize; packagesI++) { - PackageSetting packageSetting = sharedUserSetting.packages.valueAt( - packagesI); - if (packageSetting == null || packageSetting.pkg == null - || !packageSetting.getInstalled(userId)) { - continue; - } - AndroidPackage pkg = packageSetting.pkg; - requestedPermissions.addAll(pkg.getRequestedPermissions()); - targetSdkVersion = Math.min(targetSdkVersion, pkg.getTargetSdkVersion()); - } - generateFallbackPermissionsStateLpr(requestedPermissions, targetSdkVersion, - sharedUserSetting.getPermissionsState(), userId); + sharedUserSetting.getPermissionsState().setMissing(true, userId); } } } @@ -5663,30 +5645,6 @@ public final class Settings { } } - private void generateFallbackPermissionsStateLpr( - @NonNull Collection<String> requestedPermissions, int targetSdkVersion, - @NonNull PermissionsState permissionsState, @UserIdInt int userId) { - for (String permissionName : requestedPermissions) { - BasePermission permission = mPermissions.getPermission(permissionName); - if (Objects.equals(permission.getSourcePackageName(), PLATFORM_PACKAGE_NAME) - && permission.isRuntime() && !permission.isRemoved()) { - if (permission.isHardOrSoftRestricted() || permission.isImmutablyRestricted()) { - permissionsState.updatePermissionFlags(permission, userId, - PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, - PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT); - } - if (targetSdkVersion < Build.VERSION_CODES.M) { - permissionsState.updatePermissionFlags(permission, userId, - PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED - | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, - PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED - | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT); - permissionsState.grantRuntimePermission(permission, userId); - } - } - } - } - @GuardedBy("Settings.this.mLock") private void readLegacyStateForUserSyncLPr(int userId) { File permissionsFile = getUserRuntimePermissionsFile(userId); 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..b0d4d957fc21 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; @@ -154,6 +155,7 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -421,6 +423,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); } @@ -2473,13 +2479,60 @@ public class PermissionManagerService extends IPermissionManager.Stub { } final PermissionsState permissionsState = ps.getPermissionsState(); - PermissionsState origPermissions = permissionsState; final int[] currentUserIds = UserManagerService.getInstance().getUserIds(); boolean runtimePermissionsRevoked = false; int[] updatedUserIds = EMPTY_INT_ARRAY; + for (int userId : currentUserIds) { + if (permissionsState.isMissing(userId)) { + Collection<String> requestedPermissions; + int targetSdkVersion; + if (!ps.isSharedUser()) { + requestedPermissions = pkg.getRequestedPermissions(); + targetSdkVersion = pkg.getTargetSdkVersion(); + } else { + requestedPermissions = new ArraySet<>(); + targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + List<AndroidPackage> packages = ps.getSharedUser().getPackages(); + int packagesSize = packages.size(); + for (int i = 0; i < packagesSize; i++) { + AndroidPackage sharedUserPackage = packages.get(i); + requestedPermissions.addAll(sharedUserPackage.getRequestedPermissions()); + targetSdkVersion = Math.min(targetSdkVersion, + sharedUserPackage.getTargetSdkVersion()); + } + } + + for (String permissionName : requestedPermissions) { + BasePermission permission = mSettings.getPermission(permissionName); + if (Objects.equals(permission.getSourcePackageName(), PLATFORM_PACKAGE_NAME) + && permission.isRuntime() && !permission.isRemoved()) { + if (permission.isHardOrSoftRestricted() + || permission.isImmutablyRestricted()) { + permissionsState.updatePermissionFlags(permission, userId, + PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, + PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT); + } + if (targetSdkVersion < Build.VERSION_CODES.M) { + permissionsState.updatePermissionFlags(permission, userId, + PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED + | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, + PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED + | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT); + permissionsState.grantRuntimePermission(permission, userId); + } + } + } + + permissionsState.setMissing(false, userId); + updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); + } + } + + PermissionsState origPermissions = permissionsState; + boolean changedInstallPermission = false; if (replace) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java index 11e29a02068c..bad59cb1b567 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionsState.java +++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java @@ -16,6 +16,8 @@ package com.android.server.pm.permission; +import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.pm.PackageManager; import android.os.UserHandle; import android.util.ArrayMap; @@ -30,6 +32,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; /** @@ -70,6 +73,9 @@ public final class PermissionsState { private int[] mGlobalGids = NO_GIDS; + @Nullable + private SparseBooleanArray mMissing; + private SparseBooleanArray mPermissionReviewRequired; public PermissionsState() { @@ -132,6 +138,23 @@ public final class PermissionsState { other.mGlobalGids.length); } + if (mMissing != null) { + if (other.mMissing == null) { + mMissing = null; + } else { + mMissing.clear(); + } + } + if (other.mMissing != null) { + if (mMissing == null) { + mMissing = new SparseBooleanArray(); + } + final int missingSize = other.mMissing.size(); + for (int i = 0; i < missingSize; i++) { + mMissing.put(other.mMissing.keyAt(i), other.mMissing.valueAt(i)); + } + } + if (mPermissionReviewRequired != null) { if (other.mPermissionReviewRequired == null) { mPermissionReviewRequired = null; @@ -175,6 +198,10 @@ public final class PermissionsState { } } + if (!Objects.equals(mMissing, other.mMissing)) { + return false; + } + if (mPermissionReviewRequired == null) { if (other.mPermissionReviewRequired != null) { return false; @@ -185,6 +212,35 @@ public final class PermissionsState { return Arrays.equals(mGlobalGids, other.mGlobalGids); } + /** + * Check whether the permissions state is missing for a user. This can happen if permission + * state is rolled back and we'll need to generate a reasonable default state to keep the app + * usable. + */ + public boolean isMissing(@UserIdInt int userId) { + return mMissing != null && mMissing.get(userId); + } + + /** + * Set whether the permissions state is missing for a user. This can happen if permission state + * is rolled back and we'll need to generate a reasonable default state to keep the app usable. + */ + public void setMissing(boolean missing, @UserIdInt int userId) { + if (missing) { + if (mMissing == null) { + mMissing = new SparseBooleanArray(); + } + mMissing.put(userId, true); + } else { + if (mMissing != null) { + mMissing.delete(userId); + if (mMissing.size() == 0) { + mMissing = null; + } + } + } + } + public boolean isPermissionReviewRequired(int userId) { return mPermissionReviewRequired != null && mPermissionReviewRequired.get(userId); } @@ -569,6 +625,7 @@ public final class PermissionsState { invalidateCache(); } + mMissing = null; mPermissionReviewRequired = null; } diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java index 39f7ac0b5b54..9c3a39440054 100644 --- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java +++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java @@ -339,7 +339,7 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn }); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); // Don't acquire soft keyboard focus, to avoid destroying state when capturing bugreports - mDialog.getWindow().setFlags(FLAG_ALT_FOCUSABLE_IM, FLAG_ALT_FOCUSABLE_IM); + dialog.getWindow().setFlags(FLAG_ALT_FOCUSABLE_IM, FLAG_ALT_FOCUSABLE_IM); dialog.setOnDismissListener(this); diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java index d6c48a00d33d..cc369356c1c9 100644 --- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java +++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java @@ -26,7 +26,6 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INST import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.os.storage.StorageManager.PROP_LEGACY_OP_STICKY; import static java.lang.Integer.min; @@ -37,13 +36,17 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Build; -import android.os.SystemProperties; import android.os.UserHandle; +import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; +import android.provider.DeviceConfig; import com.android.server.LocalServices; import com.android.server.pm.parsing.pkg.AndroidPackage; +import java.util.Arrays; +import java.util.HashSet; + /** * The behavior of soft restricted permissions is different for each permission. This class collects * the policies in one place. @@ -65,8 +68,8 @@ public abstract class SoftRestrictedPermissionPolicy { } }; - private static final boolean isLegacyStorageAppOpStickyGlobal = SystemProperties.getBoolean( - PROP_LEGACY_OP_STICKY, /*defaultValue*/true); + private static final HashSet<String> sForcedScopedStorageAppWhitelist = new HashSet<>( + Arrays.asList(getForcedScopedStorageAppWhitelist())); /** * TargetSDK is per package. To make sure two apps int the same shared UID do not fight over @@ -141,12 +144,13 @@ public abstract class SoftRestrictedPermissionPolicy { shouldPreserveLegacyExternalStorage = pkg.hasPreserveLegacyExternalStorage() && smInternal.hasLegacyExternalStorage(appInfo.uid); targetSDK = getMinimumTargetSDK(context, appInfo, user); - // LEGACY_STORAGE op is normally sticky for apps targetig <= Q. - // However, this device can be configured to make it non-sticky. - boolean isLegacyAppOpSticky = isLegacyStorageAppOpStickyGlobal - && targetSDK <= Build.VERSION_CODES.Q; + shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0 - || (!isLegacyAppOpSticky && !shouldPreserveLegacyExternalStorage); + || (targetSDK > Build.VERSION_CODES.Q + && !shouldPreserveLegacyExternalStorage) + // If the device is configured to force this app into scoped storage, + // then we should apply the restriction + || sForcedScopedStorageAppWhitelist.contains(appInfo.packageName); } else { isWhiteListed = false; shouldApplyRestriction = false; @@ -245,6 +249,15 @@ public abstract class SoftRestrictedPermissionPolicy { return false; } + private static String[] getForcedScopedStorageAppWhitelist() { + final String rawList = DeviceConfig.getString(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, + StorageManager.PROP_FORCED_SCOPED_STORAGE_WHITELIST, /*defaultValue*/""); + if (rawList == null || rawList.equals("")) { + return new String[0]; + } + return rawList.split(","); + } + /** * @return If the permission can be granted */ 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/textclassifier/IconsContentProvider.java b/services/core/java/com/android/server/textclassifier/IconsContentProvider.java index d19a707770e2..9b3176d9df67 100644 --- a/services/core/java/com/android/server/textclassifier/IconsContentProvider.java +++ b/services/core/java/com/android/server/textclassifier/IconsContentProvider.java @@ -25,6 +25,7 @@ import android.graphics.drawable.Icon; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor.AutoCloseOutputStream; +import android.os.UserHandle; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -51,7 +52,7 @@ public final class IconsContentProvider extends ContentProvider { try { final ResourceInfo res = IconsUriHelper.getInstance().getResourceInfo(uri); final Drawable drawable = Icon.createWithResource(res.packageName, res.id) - .loadDrawable(getContext()); + .loadDrawableAsUser(getContext(), UserHandle.getCallingUserId()); final byte[] data = getBitmapData(drawable); final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); final ParcelFileDescriptor readSide = pipe[0]; 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/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index d8a4ecbbc650..1cd94b40f660 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -1387,9 +1387,7 @@ class RecentTasks { return false; } - /** - * @return whether the given task can be trimmed even if it is outside the visible range. - */ + /** @return whether the given task can be trimmed even if it is outside the visible range. */ protected boolean isTrimmable(Task task) { final ActivityStack stack = task.getStack(); @@ -1404,9 +1402,13 @@ class RecentTasks { return false; } + final ActivityStack rootHomeTask = stack.getDisplayArea().getRootHomeTask(); + // Home stack does not exist. Don't trim the task. + if (rootHomeTask == null) { + return false; + } // Trim tasks that are behind the home task. - final TaskDisplayArea taskDisplayArea = stack.getDisplayArea(); - return task.compareTo(taskDisplayArea.getRootHomeTask()) < 0; + return task.compareTo(rootHomeTask) < 0; } /** Remove the tasks that user may not be able to return. */ diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index cbc1bdfa0e9e..f1b322ed24ba 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -406,8 +406,9 @@ public class RecentsAnimationController implements DeathRecipient { } // Save the minimized home height - mMinimizedHomeBounds = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask() - .getBounds(); + final ActivityStack rootHomeTask = + mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask(); + mMinimizedHomeBounds = rootHomeTask != null ? rootHomeTask.getBounds() : null; mService.mWindowPlacerLocked.performSurfacePlacement(); diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index c02e0a11a0c5..c7f78342c829 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -387,9 +387,11 @@ class RemoteAnimationController implements DeathRecipient { int getMode() { final DisplayContent dc = mWindowContainer.getDisplayContent(); final ActivityRecord topActivity = mWindowContainer.getTopMostActivity(); + // Note that opening/closing transitions are per-activity while changing transitions + // are per-task. if (dc.mOpeningApps.contains(topActivity)) { return RemoteAnimationTarget.MODE_OPENING; - } else if (dc.mChangingContainers.contains(topActivity)) { + } else if (dc.mChangingContainers.contains(mWindowContainer)) { return RemoteAnimationTarget.MODE_CHANGING; } else { return RemoteAnimationTarget.MODE_CLOSING; 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..6661c30723d2 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -195,7 +195,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { return mChildren.indexOf(stack); } - ActivityStack getRootHomeTask() { + @Nullable ActivityStack getRootHomeTask() { return mRootHomeTask; } @@ -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..7757b3a50941 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) { @@ -2182,7 +2184,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< final int appStackClipMode = getDisplayContent().mAppTransition.getAppStackClipMode(); // Separate position and size for use in animators. - mTmpRect.set(getAnimationBounds(appStackClipMode)); + final Rect screenBounds = getAnimationBounds(appStackClipMode); + mTmpRect.set(screenBounds); getAnimationPosition(mTmpPoint); if (!sHierarchicalAnimations) { // Non-hierarchical animation uses position in global coordinates. @@ -2201,7 +2204,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< localBounds.offsetTo(mTmpPoint.x, mTmpPoint.y); final RemoteAnimationController.RemoteAnimationRecord adapters = controller.createRemoteAnimationRecord(this, mTmpPoint, localBounds, - mTmpRect, (isChanging ? mSurfaceFreezer.mFreezeBounds : null)); + screenBounds, (isChanging ? mSurfaceFreezer.mFreezeBounds : null)); resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter); } else if (isChanging) { final float durationScale = mWmService.getTransitionAnimationScaleLocked(); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index fe68cd6110f2..20b109bc06f7 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -1315,12 +1315,15 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio * * @param isCached whether or not the process is cached. */ + @HotPath(caller = HotPath.OOM_ADJUSTMENT) public void onProcCachedStateChanged(boolean isCached) { - synchronized (mAtm.mGlobalLock) { - if (!isCached && mPendingConfiguration != null) { - final Configuration config = mPendingConfiguration; - mPendingConfiguration = null; - dispatchConfigurationChange(config); + if (!isCached) { + synchronized (mAtm.mGlobalLockWithoutBoost) { + if (mPendingConfiguration != null) { + final Configuration config = mPendingConfiguration; + mPendingConfiguration = null; + dispatchConfigurationChange(config); + } } } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index e925ce5c2dac..3ec6ab41033a 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1509,7 +1509,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mActivityRecord != null ? mActivityRecord.getTask() : null; } - ActivityStack getRootTask() { + @Nullable ActivityStack getRootTask() { final Task task = getTask(); if (task != null) { return (ActivityStack) task.getRootTask(); @@ -2527,7 +2527,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final Task task = getTask(); if (task != null) { task.getDimBounds(mTmpRect); - } else { + } else if (getRootTask() != null) { getRootTask().getDimBounds(mTmpRect); } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index c570cf1d949f..b30d40824a05 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1257,19 +1257,25 @@ class WindowStateAnimator { mYOffset = dy; mWallpaperScale = scale; - try { - if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset"); - mService.openSurfaceTransaction(); - setWallpaperPositionAndScale(dx, dy, scale, false); - } catch (RuntimeException e) { - Slog.w(TAG, "Error positioning surface of " + mWin - + " pos=(" + dx + "," + dy + ")", e); - } finally { - mService.closeSurfaceTransaction("setWallpaperOffset"); - if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, - "<<< CLOSE TRANSACTION setWallpaperOffset"); - return true; + if (mSurfaceController != null) { + try { + if (SHOW_LIGHT_TRANSACTIONS) { + Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset"); + } + mService.openSurfaceTransaction(); + setWallpaperPositionAndScale(dx, dy, scale, false); + } catch (RuntimeException e) { + Slog.w(TAG, "Error positioning surface of " + mWin + + " pos=(" + dx + "," + dy + ")", e); + } finally { + mService.closeSurfaceTransaction("setWallpaperOffset"); + if (SHOW_LIGHT_TRANSACTIONS) { + Slog.i(TAG, "<<< CLOSE TRANSACTION setWallpaperOffset"); + } + } } + + return true; } private void setWallpaperPositionAndScale(int dx, int dy, float scale, 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/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index 285caf34ae67..48ec529c9a45 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -115,6 +115,8 @@ public class BiometricServiceTest { public void setUp() { MockitoAnnotations.initMocks(this); + resetReceivers(); + when(mContext.getContentResolver()).thenReturn(mContentResolver); when(mContext.getResources()).thenReturn(mResources); when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)) @@ -147,6 +149,74 @@ public class BiometricServiceTest { } @Test + public void testClientBinderDied_whenPaused() throws Exception { + setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + + invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, + true /* requireConfirmation */, null /* authenticators */); + waitForIdle(); + verify(mReceiver1.asBinder()).linkToDeath(eq(mBiometricService.mCurrentAuthSession), + anyInt()); + + mBiometricService.mInternalReceiver.onError( + getCookieForCurrentSession(mBiometricService.mCurrentAuthSession), + BiometricAuthenticator.TYPE_FACE, + BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, + 0 /* vendorCode */); + waitForIdle(); + + assertEquals(BiometricService.STATE_AUTH_PAUSED, + mBiometricService.mCurrentAuthSession.mState); + + mBiometricService.mCurrentAuthSession.binderDied(); + waitForIdle(); + + assertNull(mBiometricService.mCurrentAuthSession); + verify(mBiometricService.mStatusBarService).hideAuthenticationDialog(); + verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt()); + } + + @Test + public void testClientBinderDied_whenAuthenticating() throws Exception { + setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + + invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, + true /* requireConfirmation */, null /* authenticators */); + waitForIdle(); + verify(mReceiver1.asBinder()).linkToDeath(eq(mBiometricService.mCurrentAuthSession), + anyInt()); + + assertEquals(BiometricService.STATE_AUTH_STARTED, + mBiometricService.mCurrentAuthSession.mState); + mBiometricService.mCurrentAuthSession.binderDied(); + waitForIdle(); + + assertNotNull(mBiometricService.mCurrentAuthSession); + verify(mBiometricService.mStatusBarService, never()).hideAuthenticationDialog(); + assertEquals(BiometricService.STATE_CLIENT_DIED_CANCELLING, + mBiometricService.mCurrentAuthSession.mState); + + verify(mBiometricService.mAuthenticators.get(0).impl).cancelAuthenticationFromService( + any(), + any(), + anyInt(), + anyInt(), + anyInt(), + eq(false) /* fromClient */); + + // Simulate ERROR_CANCELED received from HAL + mBiometricService.mInternalReceiver.onError( + getCookieForCurrentSession(mBiometricService.mCurrentAuthSession), + BiometricAuthenticator.TYPE_FACE, + BiometricConstants.BIOMETRIC_ERROR_CANCELED, + 0 /* vendorCode */); + waitForIdle(); + verify(mBiometricService.mStatusBarService).hideAuthenticationDialog(); + verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt()); + assertNull(mBiometricService.mCurrentAuthSession); + } + + @Test public void testAuthenticate_credentialAllowedButNotSetup_returnsNoDeviceCredential() throws Exception { when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(false); @@ -311,7 +381,7 @@ public class BiometricServiceTest { eq(0 /* vendorCode */)); // Enrolled, not disabled in settings, user requires confirmation in settings - resetReceiver(); + resetReceivers(); when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true); when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt())) .thenReturn(true); @@ -332,7 +402,7 @@ public class BiometricServiceTest { anyInt() /* callingUserId */); // Enrolled, not disabled in settings, user doesn't require confirmation in settings - resetReceiver(); + resetReceivers(); when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt())) .thenReturn(false); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, @@ -1198,7 +1268,7 @@ public class BiometricServiceTest { eq(0) /* vendorCode */); // Request for weak auth works - resetReceiver(); + resetReceivers(); authenticators = Authenticators.BIOMETRIC_WEAK; assertEquals(BiometricManager.BIOMETRIC_SUCCESS, invokeCanAuthenticate(mBiometricService, authenticators)); @@ -1217,7 +1287,7 @@ public class BiometricServiceTest { anyInt() /* sysUiSessionId */); // Requesting strong and credential, when credential is setup - resetReceiver(); + resetReceivers(); authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL; when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true); assertEquals(BiometricManager.BIOMETRIC_SUCCESS, @@ -1244,7 +1314,7 @@ public class BiometricServiceTest { } } - resetReceiver(); + resetReceivers(); authenticators = Authenticators.BIOMETRIC_STRONG; assertEquals(BiometricManager.BIOMETRIC_SUCCESS, invokeCanAuthenticate(mBiometricService, authenticators)); @@ -1449,9 +1519,12 @@ public class BiometricServiceTest { } } - private void resetReceiver() { + private void resetReceivers() { mReceiver1 = mock(IBiometricServiceReceiver.class); mReceiver2 = mock(IBiometricServiceReceiver.class); + + when(mReceiver1.asBinder()).thenReturn(mock(Binder.class)); + when(mReceiver2.asBinder()).thenReturn(mock(Binder.class)); } private void resetStatusBar() { 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. |