Merge "Fix crash caused by unhandled bucket" into rvc-dev
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 2834ab1..94e5d0b 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 @@
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/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 21ffff3..d865c21 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -122,11 +122,11 @@
}
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 f9a8842..26b3d3c 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -53,8 +53,8 @@
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 0de92f3..6633659 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -161,13 +161,12 @@
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 @@
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 6f84076..53f0f28 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -55,8 +55,8 @@
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 28563ad..5fabb5f 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -182,8 +182,8 @@
};
void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey, const int32_t oldState,
- const int32_t newState){};
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState){};
// Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp.
// This method clears all the past buckets.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index bf636a4..f03ce45 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -182,15 +182,26 @@
}
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 c8dc8cc..751fef2 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -90,7 +90,7 @@
};
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 d1af196..6388001 100644
--- a/cmds/statsd/src/state/StateListener.h
+++ b/cmds/statsd/src/state/StateListener.h
@@ -45,8 +45,8 @@
* [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 b63713b..41e525c 100644
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -35,31 +35,30 @@
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 @@
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 @@
// 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::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 @@
// 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 c5f6315..abd579e 100644
--- a/cmds/statsd/src/state/StateTracker.h
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -72,19 +72,19 @@
std::set<wp<StateListener>> mListeners;
// Reset all state values in map to the given state.
- void handleReset(const int64_t eventTimeNs, const int32_t newState);
+ void handleReset(const int64_t eventTimeNs, const FieldValue& newState);
// Clears the state value mapped to the given primary key by setting it to kStateUnknown.
void clearStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey);
// Update the StateMap based on the received state value.
void updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
- const int32_t newState, const bool nested,
+ const FieldValue& newState, const bool nested,
StateValueInfo* stateValueInfo);
// Notify registered state listeners of state change.
void notifyListeners(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
- const int32_t oldState, const int32_t newState);
+ const FieldValue& oldState, const FieldValue& newState);
};
bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index b6e1075..474aa22 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -3898,14 +3898,12 @@
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 @@
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(true, it->second[0].hasValue);
- EXPECT_EQ(4, it->second[0].value.long_value);
+ 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);
+
+ // 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}
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 13e8f5c..530ac5e 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -50,8 +50,9 @@
std::vector<Update> updates;
void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey, int oldState, int newState) {
- updates.emplace_back(primaryKey, newState);
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState) {
+ updates.emplace_back(primaryKey, newState.mValue.int_value);
}
};
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index eea1d69..ea8b190 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5902,6 +5902,12 @@
}
}
+ /**
+ * 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 +5924,22 @@
}
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 +5950,18 @@
}
/**
- * 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 +5972,13 @@
&& 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;
}
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 0d4e16b..8b52242 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -19,6 +19,7 @@
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 @@
@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 @@
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 @@
@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 f6d3dbd..9a457a3 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -18,6 +18,7 @@
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 @@
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 @@
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 @@
@Override
public void recycle() {
mTargetDisplayId = 0;
- mConfiguration = null;
+ mConfiguration = Configuration.EMPTY;
ObjectPool.recycle(this);
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 4bed985..f9ed2f8 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -314,6 +314,92 @@
}
@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/res/res/values/config.xml b/core/res/res/values/config.xml
index b62bb33..3730f05 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -44,6 +44,7 @@
<item><xliff:g id="id">@string/status_bar_secure</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_screen_record</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_camera</xliff:g></item>
@@ -59,7 +60,6 @@
<item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_sensors_off</xliff:g></item>
- <item><xliff:g id="id">@string/status_bar_screen_record</xliff:g></item>
</string-array>
<string translatable="false" name="status_bar_rotate">rotate</string>
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index a93dacf..000e870 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_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.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.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 @@
}
@Test
- public void testHandleActivityConfigurationChanged_PickNewerPendingConfiguration() {
+ public void testHandleActivityConfigurationChanged_SkipWhenNewerConfigurationPending() {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
@@ -237,26 +240,91 @@
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
public void testHandleActivityConfigurationChanged_OnlyAppliesNewestConfiguration()
throws Exception {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
@@ -268,7 +336,7 @@
config.orientation = ORIENTATION_PORTRAIT;
activityThread.handleActivityConfigurationChanged(activity.getActivityToken(),
- config, Display.INVALID_DISPLAY);
+ config, INVALID_DISPLAY);
});
final int numOfConfig = activity.mNumOfConfigChanges;
@@ -504,7 +572,7 @@
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 @@
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 @@
}
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 6c23125..4654f63 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 @@
@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 @@
@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/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 72827a9..a5a2221 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/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 83d7218..b85c771 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 5f9563a..0000000
--- 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 bd97561..0000000
--- 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 4510b87..055b2be 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 0000000..62ebbaa
--- /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/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index b461295..4f42370 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/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 1369350..5a78c90 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 @@
// 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 @@
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 @@
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 @@
* 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/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 6274467..d69c3b0 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 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.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 @@
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 @@
@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 @@
mBlurUtils = blurUtils;
mRingerModeTracker = ringerModeTracker;
mControlsController = controlsController;
+ mSysUiState = sysUiState;
mMainHandler = handler;
// receive broadcasts
@@ -638,7 +643,7 @@
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 @@
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 @@
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 @@
mNotificationShadeWindowController = notificationShadeWindowController;
mControlsUiController = controlsUiController;
mBlurUtils = blurUtils;
+ mSysUiState = sysuiState;
mUseControlsLayout = useControlsLayout;
mOnRotateCallback = onRotateCallback;
mKeyguardShowing = keyguardShowing;
@@ -2203,6 +2211,8 @@
mShowing = true;
mHadTopUi = mNotificationShadeWindowController.getForceHasTopUi();
mNotificationShadeWindowController.setForceHasTopUi(true);
+ mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
+ .commitUpdate(mContext.getDisplayId());
ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView();
root.setOnApplyWindowInsetsListener((v, windowInsets) -> {
@@ -2303,6 +2313,8 @@
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/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 839ea69..6f68ee8 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 @@
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);
@@ -712,18 +709,23 @@
}
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 +746,11 @@
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 +758,12 @@
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 +777,13 @@
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/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index ee7733a..4a85980 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.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 @@
@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 @@
mControlsController,
mUiEventLogger,
mRingerModeTracker,
+ mSysUiState,
mHandler
);
mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 45023ac..c6e1c95 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.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 @@
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 @@
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 @@
return;
}
}
- SystemClock.sleep(100);
+ SystemClock.sleep(DELAY_MS);
}
}
@@ -233,7 +238,7 @@
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 @@
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 @@
};
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 @@
}
@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/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index e410220..2d45f9e 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 @@
@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/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 88de34d..bdcae48 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 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.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 @@
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);