Merge "Only clear mInputMethodInputTarget if it is moved to the new display" into rvc-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index f8b2f32..ac58f3d 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -1224,7 +1224,7 @@
IDLE_FACTOR = mParser.getFloat(KEY_IDLE_FACTOR,
2f);
MIN_TIME_TO_ALARM = mParser.getDurationMillis(KEY_MIN_TIME_TO_ALARM,
- !COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L);
+ !COMPRESS_TIME ? 30 * 60 * 1000L : 6 * 60 * 1000L);
MAX_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
KEY_MAX_TEMP_APP_WHITELIST_DURATION, 5 * 60 * 1000L);
MMS_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 92e09ea..e251399 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -181,6 +181,7 @@
return false;
}
+
bool matches(const Matcher& that) const;
};
@@ -360,7 +361,9 @@
class Annotations {
public:
- Annotations() {}
+ Annotations() {
+ setNested(true); // Nested = true by default
+ }
// This enum stores where particular annotations can be found in the
// bitmask. Note that these pos do not correspond to annotation ids.
@@ -379,7 +382,9 @@
inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); }
- inline void setResetState(int resetState) { mResetState = resetState; }
+ inline void setResetState(int32_t resetState) {
+ mResetState = resetState;
+ }
// Default value = false
inline bool isNested() const { return getValueFromBitmask(NESTED_POS); }
@@ -395,7 +400,9 @@
// If a reset state is not sent in the StatsEvent, returns -1. Note that a
// reset satate is only sent if and only if a reset should be triggered.
- inline int getResetState() const { return mResetState; }
+ inline int32_t getResetState() const {
+ return mResetState;
+ }
private:
inline void setBitmaskAtPos(int pos, bool value) {
@@ -411,7 +418,7 @@
// there are only 4 booleans, just one byte is required.
uint8_t mBooleanBitmask = 0;
- int mResetState = -1;
+ int32_t mResetState = -1;
};
/**
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 29249f4..eba66e0 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -180,6 +180,23 @@
return num_matches > 0;
}
+bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output) {
+ size_t num_matches = 0;
+ const int32_t simpleFieldMask = 0xff7f0000;
+ const int32_t attributionUidFieldMask = 0xff7f7f7f;
+ for (const auto& value : values) {
+ if (value.mAnnotations.isPrimaryField()) {
+ output->addValue(value);
+ output->mutableValue(num_matches)->mField.setTag(value.mField.getTag());
+ const int32_t mask =
+ isAttributionUidField(value) ? attributionUidFieldMask : simpleFieldMask;
+ output->mutableValue(num_matches)->mField.setField(value.mField.getField() & mask);
+ num_matches++;
+ }
+ }
+ return num_matches > 0;
+}
+
void filterGaugeValues(const std::vector<Matcher>& matcherFields,
const std::vector<FieldValue>& values, std::vector<FieldValue>* output) {
for (const auto& field : matcherFields) {
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 33a5024..bd01100 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -154,6 +154,18 @@
HashableDimensionKey* output);
/**
+ * Creating HashableDimensionKeys from State Primary Keys in FieldValues.
+ *
+ * This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
+ * in it. This is because: for example, when we create dimension from last uid in attribution chain,
+ * In one event, uid 1000 is at position 5 and it's the last
+ * In another event, uid 1000 is at position 6, and it's the last
+ * these 2 events should be mapped to the same dimension. So we will remove the original position
+ * from the dimension key for the uid field (by applying 0x80 bit mask).
+ */
+bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output);
+
+/**
* Filter the values from FieldValues using the matchers.
*
* In contrast to the above function, this function will not do any modification to the original
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 8b6a864..61cd017 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -293,7 +293,8 @@
}
const bool exclusiveState = readNextValue<uint8_t>();
- mValues[mValues.size() - 1].mAnnotations.setExclusiveState(exclusiveState);
+ mExclusiveStateFieldIndex = mValues.size() - 1;
+ mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState);
}
void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 4eeb7d6..41fdcc2 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -145,7 +145,7 @@
}
// Default value = false
- inline bool shouldTruncateTimestamp() {
+ inline bool shouldTruncateTimestamp() const {
return mTruncateTimestamp;
}
@@ -170,6 +170,20 @@
return mAttributionChainIndex;
}
+ // Returns the index of the exclusive state field within the FieldValues vector if
+ // an exclusive state exists. If there is no exclusive state field, returns -1.
+ //
+ // If the index within the atom definition is desired, do the following:
+ // int vectorIndex = LogEvent.getExclusiveStateFieldIndex();
+ // if (vectorIndex != -1) {
+ // FieldValue& v = LogEvent.getValues()[vectorIndex];
+ // int atomIndex = v.mField.getPosAtDepth(0);
+ // }
+ // Note that atomIndex is 1-indexed.
+ inline int getExclusiveStateFieldIndex() const {
+ return mExclusiveStateFieldIndex;
+ }
+
inline LogEvent makeCopy() {
return LogEvent(*this);
}
@@ -297,6 +311,7 @@
bool mTruncateTimestamp = false;
int mUidFieldIndex = -1;
int mAttributionChainIndex = -1;
+ int mExclusiveStateFieldIndex = -1;
};
void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 6833f8d..d68f64a 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -151,8 +151,7 @@
uint64_t wrapperToken =
mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
- const int64_t elapsedTimeNs = truncateTimestampIfNecessary(
- event.GetTagId(), event.GetElapsedTimestampNs());
+ const int64_t elapsedTimeNs = truncateTimestampIfNecessary(event);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, (long long) elapsedTimeNs);
uint64_t eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS);
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 42bbd8e..c4bd054 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -270,11 +270,9 @@
protoOutput->end(atomsToken);
}
for (const auto& atom : bucket.mGaugeAtoms) {
- const int64_t elapsedTimestampNs =
- truncateTimestampIfNecessary(mAtomId, atom.mElapsedTimestamps);
- protoOutput->write(
- FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
- (long long)elapsedTimestampNs);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED |
+ FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
+ (long long)atom.mElapsedTimestampNs);
}
}
protoOutput->end(bucketInfoToken);
@@ -477,7 +475,9 @@
if ((*mCurrentSlicedBucket)[eventKey].size() >= mGaugeAtomsPerDimensionLimit) {
return;
}
- GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs);
+
+ const int64_t truncatedElapsedTimestampNs = truncateTimestampIfNecessary(event);
+ GaugeAtom gaugeAtom(getGaugeFields(event), truncatedElapsedTimestampNs);
(*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom);
// Anomaly detection on gauge metric only works when there is one numeric
// field specified.
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 79ec711..aa0cae2 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -35,10 +35,10 @@
struct GaugeAtom {
GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs)
- : mFields(fields), mElapsedTimestamps(elapsedTimeNs) {
+ : mFields(fields), mElapsedTimestampNs(elapsedTimeNs) {
}
std::shared_ptr<vector<FieldValue>> mFields;
- int64_t mElapsedTimestamps;
+ int64_t mElapsedTimestampNs;
};
struct GaugeBucket {
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 4550e65..6aba13ca 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -451,7 +451,7 @@
std::vector<int32_t> mSlicedStateAtoms;
// Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>).
- std::unordered_map<int32_t, std::unordered_map<int, int64_t>> mStateGroupMap;
+ const std::unordered_map<int32_t, std::unordered_map<int, int64_t>> mStateGroupMap;
// MetricStateLinks defined in statsd_config that link fields in the state
// atom to fields in the "what" atom.
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 2fcb13b..88616dd 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -795,9 +795,7 @@
for (const auto& it : allMetricProducers) {
// Register metrics to StateTrackers
for (int atomId : it->getSlicedStateAtoms()) {
- if (!StateManager::getInstance().registerListener(atomId, it)) {
- return false;
- }
+ StateManager::getInstance().registerListener(atomId, it);
}
}
return true;
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
index ea776fa..5514b446 100644
--- a/cmds/statsd/src/state/StateManager.cpp
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -38,20 +38,12 @@
}
}
-bool StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
+void StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
// Check if state tracker already exists.
if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
- // Create a new state tracker iff atom is a state atom.
- auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(atomId);
- if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
- mStateTrackers[atomId] = new StateTracker(atomId, it->second);
- } else {
- ALOGE("StateManager cannot register listener, Atom %d is not a state atom", atomId);
- return false;
- }
+ mStateTrackers[atomId] = new StateTracker(atomId);
}
mStateTrackers[atomId]->registerListener(listener);
- return true;
}
void StateManager::unregisterListener(const int32_t atomId, wp<StateListener> listener) {
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
index 8b3a421..577a0f5 100644
--- a/cmds/statsd/src/state/StateManager.h
+++ b/cmds/statsd/src/state/StateManager.h
@@ -45,10 +45,11 @@
// Notifies the correct StateTracker of an event.
void onLogEvent(const LogEvent& event);
- // Returns true if atomId is being tracked and is associated with a state
- // atom. StateManager notifies the correct StateTracker to register listener.
+ // Notifies the StateTracker for the given atomId to register listener.
// If the correct StateTracker does not exist, a new StateTracker is created.
- bool registerListener(const int32_t atomId, wp<StateListener> listener);
+ // Note: StateTrackers can be created for non-state atoms. They are essentially empty and
+ // do not perform any actions.
+ void registerListener(const int32_t atomId, wp<StateListener> listener);
// Notifies the correct StateTracker to unregister a listener
// and removes the tracker if it no longer has any listeners.
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
index ab86127..b7f314a 100644
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -25,81 +25,43 @@
namespace os {
namespace statsd {
-StateTracker::StateTracker(const int32_t atomId, const util::StateAtomFieldOptions& stateAtomInfo)
- : mAtomId(atomId),
- mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)),
- mNested(stateAtomInfo.nested) {
- // create matcher for each primary field
- for (const auto& primaryField : stateAtomInfo.primaryFields) {
- if (primaryField == util::FIRST_UID_IN_CHAIN) {
- Matcher matcher = getFirstUidMatcher(atomId);
- mPrimaryFields.push_back(matcher);
- } else {
- Matcher matcher = getSimpleMatcher(atomId, primaryField);
- mPrimaryFields.push_back(matcher);
- }
- }
-
- if (stateAtomInfo.defaultState != util::UNSET_VALUE) {
- mDefaultState = stateAtomInfo.defaultState;
- }
-
- if (stateAtomInfo.resetState != util::UNSET_VALUE) {
- mResetState = stateAtomInfo.resetState;
- }
+StateTracker::StateTracker(const int32_t atomId) : mField(atomId, 0) {
}
void StateTracker::onLogEvent(const LogEvent& event) {
- int64_t eventTimeNs = event.GetElapsedTimestampNs();
+ const int64_t eventTimeNs = event.GetElapsedTimestampNs();
// Parse event for primary field values i.e. primary key.
HashableDimensionKey primaryKey;
- if (mPrimaryFields.size() > 0) {
- if (!filterValues(mPrimaryFields, event.getValues(), &primaryKey) ||
- primaryKey.getValues().size() != mPrimaryFields.size()) {
- ALOGE("StateTracker error extracting primary key from log event.");
- handleReset(eventTimeNs);
- return;
- }
- } else {
- // Use an empty HashableDimensionKey if atom has no primary fields.
- primaryKey = DEFAULT_DIMENSION_KEY;
+ filterPrimaryKey(event.getValues(), &primaryKey);
+
+ FieldValue stateValue;
+ if (!getStateFieldValueFromLogEvent(event, &stateValue)) {
+ ALOGE("StateTracker error extracting state from log event. Missing exclusive state field.");
+ clearStateForPrimaryKey(eventTimeNs, primaryKey);
+ return;
}
- // Parse event for state value.
- FieldValue stateValue;
- if (!filterValues(mStateField, event.getValues(), &stateValue) ||
- stateValue.mValue.getType() != INT) {
+ mField.setField(stateValue.mField.getField());
+
+ if (stateValue.mValue.getType() != INT) {
ALOGE("StateTracker error extracting state from log event. Type: %d",
stateValue.mValue.getType());
- handlePartialReset(eventTimeNs, primaryKey);
+ clearStateForPrimaryKey(eventTimeNs, primaryKey);
return;
}
- int32_t state = stateValue.mValue.int_value;
- if (state == mResetState) {
- VLOG("StateTracker Reset state: %s", stateValue.mValue.toString().c_str());
- handleReset(eventTimeNs);
+ const int32_t resetState = stateValue.mAnnotations.getResetState();
+ if (resetState != -1) {
+ VLOG("StateTracker new reset state: %d", resetState);
+ handleReset(eventTimeNs, resetState);
return;
}
- // Track and update state.
- int32_t oldState = 0;
- int32_t newState = 0;
- updateState(primaryKey, state, &oldState, &newState);
-
- // Notify all listeners if state has changed.
- if (oldState != newState) {
- VLOG("StateTracker updated state");
- for (auto listener : mListeners) {
- auto sListener = listener.promote(); // safe access to wp<>
- if (sListener != nullptr) {
- sListener->onStateChanged(eventTimeNs, mAtomId, primaryKey, oldState, newState);
- }
- }
- } else {
- VLOG("StateTracker NO updated state");
- }
+ const int32_t newState = stateValue.mValue.int_value;
+ const bool nested = stateValue.mAnnotations.isNested();
+ StateValueInfo* stateValueInfo = &mStateMap[primaryKey];
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo);
}
void StateTracker::registerListener(wp<StateListener> listener) {
@@ -111,81 +73,62 @@
}
bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const {
- output->mField = mStateField.mMatcher;
+ output->mField = mField;
- // Check that the query key has the correct number of primary fields.
- if (queryKey.getValues().size() == mPrimaryFields.size()) {
- auto it = mStateMap.find(queryKey);
- if (it != mStateMap.end()) {
- output->mValue = it->second.state;
- return true;
- }
- } else if (queryKey.getValues().size() > mPrimaryFields.size()) {
- ALOGE("StateTracker query key size %zu > primary key size %zu is illegal",
- queryKey.getValues().size(), mPrimaryFields.size());
- } else {
- ALOGE("StateTracker query key size %zu < primary key size %zu is not supported",
- queryKey.getValues().size(), mPrimaryFields.size());
+ if (const auto it = mStateMap.find(queryKey); it != mStateMap.end()) {
+ output->mValue = it->second.state;
+ return true;
}
- // Set the state value to default state if:
- // - query key size is incorrect
- // - query key is not found in state map
- output->mValue = mDefaultState;
+ // Set the state value to kStateUnknown if query key is not found in state map.
+ output->mValue = kStateUnknown;
return false;
}
-void StateTracker::handleReset(const int64_t eventTimeNs) {
+void StateTracker::handleReset(const int64_t eventTimeNs, const int32_t newState) {
VLOG("StateTracker handle reset");
- for (const auto pair : mStateMap) {
- for (auto l : mListeners) {
- auto sl = l.promote();
- if (sl != nullptr) {
- sl->onStateChanged(eventTimeNs, mAtomId, pair.first, pair.second.state,
- mDefaultState);
- }
- }
- }
- mStateMap.clear();
-}
-
-void StateTracker::handlePartialReset(const int64_t eventTimeNs,
- const HashableDimensionKey& primaryKey) {
- VLOG("StateTracker handle partial reset");
- if (mStateMap.find(primaryKey) != mStateMap.end()) {
- for (auto l : mListeners) {
- auto sl = l.promote();
- if (sl != nullptr) {
- sl->onStateChanged(eventTimeNs, mAtomId, primaryKey,
- mStateMap.find(primaryKey)->second.state, mDefaultState);
- }
- }
- mStateMap.erase(primaryKey);
+ for (auto& [primaryKey, stateValueInfo] : mStateMap) {
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, newState,
+ false /* nested; treat this state change as not nested */,
+ &stateValueInfo);
}
}
-void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int32_t eventState,
- int32_t* oldState, int32_t* newState) {
- // get old state (either current state in map or default state)
- auto it = mStateMap.find(primaryKey);
+void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs,
+ const HashableDimensionKey& primaryKey) {
+ VLOG("StateTracker clear state for primary key");
+ const std::unordered_map<HashableDimensionKey, StateValueInfo>::iterator it =
+ mStateMap.find(primaryKey);
+
+ // If there is no entry for the primaryKey in mStateMap, then the state is already
+ // kStateUnknown.
if (it != mStateMap.end()) {
- *oldState = it->second.state;
- } else {
- *oldState = mDefaultState;
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, kStateUnknown,
+ false /* nested; treat this state change as not nested */,
+ &it->second);
+ }
+}
+
+void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs,
+ const HashableDimensionKey& primaryKey,
+ const int32_t newState, const bool nested,
+ StateValueInfo* stateValueInfo) {
+ const int32_t oldState = stateValueInfo->state;
+
+ if (kStateUnknown == newState) {
+ mStateMap.erase(primaryKey);
}
// Update state map for non-nested counting case.
// Every state event triggers a state overwrite.
- if (!mNested) {
- if (eventState == mDefaultState) {
- // remove (key, state) pair if state returns to default state
- VLOG("\t StateTracker changed to default state")
- mStateMap.erase(primaryKey);
- } else {
- mStateMap[primaryKey].state = eventState;
- mStateMap[primaryKey].count = 1;
+ if (!nested) {
+ stateValueInfo->state = newState;
+ stateValueInfo->count = 1;
+
+ // Notify listeners if state has changed.
+ if (oldState != newState) {
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
}
- *newState = eventState;
return;
}
@@ -197,31 +140,47 @@
// number of OFF events as ON events.
//
// In atoms.proto, a state atom with nested counting enabled
- // must only have 2 states and one of the states must be the default state.
- it = mStateMap.find(primaryKey);
- if (it != mStateMap.end()) {
- *newState = it->second.state;
- if (eventState == it->second.state) {
- it->second.count++;
- } else if (eventState == mDefaultState) {
- if ((--it->second.count) == 0) {
- mStateMap.erase(primaryKey);
- *newState = mDefaultState;
- }
- } else {
- ALOGE("StateTracker Nest counting state has a third state instead of the binary state "
- "limit.");
- return;
+ // 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) {
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
}
- } else {
- if (eventState != mDefaultState) {
- mStateMap[primaryKey].state = eventState;
- mStateMap[primaryKey].count = 1;
- }
- *newState = eventState;
+ } else if (oldState == kStateUnknown) {
+ stateValueInfo->state = newState;
+ stateValueInfo->count = 1;
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
+ } else if (oldState == newState) {
+ stateValueInfo->count++;
+ } else if (--stateValueInfo->count == 0) {
+ stateValueInfo->state = newState;
+ 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) {
+ for (auto l : mListeners) {
+ auto sl = l.promote();
+ if (sl != nullptr) {
+ sl->onStateChanged(eventTimeNs, mField.getTag(), primaryKey, oldState, newState);
+ }
+ }
+}
+
+bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output) {
+ const int exclusiveStateFieldIndex = event.getExclusiveStateFieldIndex();
+ if (-1 == exclusiveStateFieldIndex) {
+ ALOGE("error extracting state from log event. Missing exclusive state field.");
+ return false;
+ }
+
+ *output = event.getValues()[exclusiveStateFieldIndex];
+ return true;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
index 154750e..c5f6315 100644
--- a/cmds/statsd/src/state/StateTracker.h
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -15,7 +15,6 @@
*/
#pragma once
-#include <atoms_info.h>
#include <utils/RefBase.h>
#include "HashableDimensionKey.h"
#include "logd/LogEvent.h"
@@ -30,7 +29,7 @@
class StateTracker : public virtual RefBase {
public:
- StateTracker(const int32_t atomId, const android::util::StateAtomFieldOptions& stateAtomInfo);
+ StateTracker(const int32_t atomId);
virtual ~StateTracker(){};
@@ -60,21 +59,11 @@
private:
struct StateValueInfo {
- int32_t state; // state value
- int count; // nested count (only used for binary states)
+ int32_t state = kStateUnknown; // state value
+ int count = 0; // nested count (only used for binary states)
};
- const int32_t mAtomId; // id of the state atom being tracked
-
- Matcher mStateField; // matches the atom's exclusive state field
-
- std::vector<Matcher> mPrimaryFields; // matches the atom's primary fields
-
- int32_t mDefaultState = kStateUnknown;
-
- int32_t mResetState = kStateUnknown;
-
- const bool mNested;
+ Field mField;
// Maps primary key to state value info
std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap;
@@ -82,20 +71,24 @@
// Set of all StateListeners (objects listening for state changes)
std::set<wp<StateListener>> mListeners;
- // Reset all state values in map to default state.
- void handleReset(const int64_t eventTimeNs);
+ // Reset all state values in map to the given state.
+ void handleReset(const int64_t eventTimeNs, const int32_t newState);
- // Reset only the state value mapped to the given primary key to default state.
- // Partial resets are used when we only need to update the state of one primary
- // key instead of clearing/reseting every key in the map.
- void handlePartialReset(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey);
+ // 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.
- // Store the old and new states.
- void updateState(const HashableDimensionKey& primaryKey, const int32_t eventState,
- int32_t* oldState, int32_t* newState);
+ void updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
+ const int32_t 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);
};
+bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output);
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 77a3eb3..5635313 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -549,14 +549,13 @@
return time(nullptr) * MS_PER_SEC;
}
-int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs) {
- if (AtomsInfo::kTruncatingTimestampAtomBlackList.find(atomId) !=
- AtomsInfo::kTruncatingTimestampAtomBlackList.end() ||
- (atomId >= StatsdStats::kTimestampTruncationStartTag &&
- atomId <= StatsdStats::kTimestampTruncationEndTag)) {
- return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
+int64_t truncateTimestampIfNecessary(const LogEvent& event) {
+ if (event.shouldTruncateTimestamp() ||
+ (event.GetTagId() >= StatsdStats::kTimestampTruncationStartTag &&
+ event.GetTagId() <= StatsdStats::kTimestampTruncationEndTag)) {
+ return event.GetElapsedTimestampNs() / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
} else {
- return timestampNs;
+ return event.GetElapsedTimestampNs();
}
}
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index ade25d6..20d93b5 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -17,11 +17,12 @@
#pragma once
#include <android/util/ProtoOutputStream.h>
+
#include "FieldValue.h"
#include "HashableDimensionKey.h"
-#include "atoms_info.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
using android::util::ProtoOutputStream;
@@ -93,9 +94,9 @@
return message->ParseFromArray(pbBytes.c_str(), pbBytes.size());
}
-// Checks the blacklist of atoms as well as the blacklisted range of 300,000 - 304,999.
+// Checks the truncate timestamp annotation as well as the blacklisted range of 300,000 - 304,999.
// Returns the truncated timestamp to the nearest 5 minutes if needed.
-int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs);
+int64_t truncateTimestampIfNecessary(const LogEvent& event);
// Checks permission for given pid and uid.
bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid);
diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
index a5da9c8..b1461a1 100644
--- a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
@@ -232,7 +232,7 @@
StateMap map = state.map();
for (auto group : map.group()) {
for (auto value : group.value()) {
- EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value],
+ EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value),
group.group_id());
}
}
@@ -614,7 +614,7 @@
StateMap map = state1.map();
for (auto group : map.group()) {
for (auto value : group.value()) {
- EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value],
+ EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value),
group.group_id());
}
}
diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
index ba09a35..d59ec3e 100644
--- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
@@ -921,18 +921,18 @@
bucket #1 bucket #2
| 1 2 3 4 5 6 7 8 (minutes)
|---------------------------------------|------------------
- ON OFF ON (BatterySaverMode)
+ ON OFF ON (BatterySaverMode)
T F T (DeviceUnpluggedPredicate)
- | | | (ScreenIsOnEvent)
+ | | | (ScreenIsOnEvent)
| | | (ScreenIsOffEvent)
| (ScreenDozeEvent)
*/
// Initialize log events.
std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30
events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 60 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 1:10
+ bucketStartTimeNs + 20 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:30
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10
events.push_back(CreateScreenStateChangedEvent(
bucketStartTimeNs + 80 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:30
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index 78c80bc..ba2a4cf 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -19,6 +19,7 @@
#include "state/StateListener.h"
#include "state/StateManager.h"
+#include "state/StateTracker.h"
#include "stats_event.h"
#include "tests/statsd_test_util.h"
@@ -127,23 +128,23 @@
// Register listener to non-existing StateTracker
EXPECT_EQ(0, mgr.getStateTrackersCount());
- EXPECT_TRUE(mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1));
+ mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
EXPECT_EQ(1, mgr.getStateTrackersCount());
EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
// Register listener to existing StateTracker
- EXPECT_TRUE(mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2));
+ mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2);
EXPECT_EQ(1, mgr.getStateTrackersCount());
EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
// Register already registered listener to existing StateTracker
- EXPECT_TRUE(mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2));
+ mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2);
EXPECT_EQ(1, mgr.getStateTrackersCount());
EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
// Register listener to non-state atom
- EXPECT_FALSE(mgr.registerListener(util::BATTERY_LEVEL_CHANGED, listener2));
- EXPECT_EQ(1, mgr.getStateTrackersCount());
+ mgr.registerListener(util::BATTERY_LEVEL_CHANGED, listener2);
+ EXPECT_EQ(2, mgr.getStateTrackersCount());
}
/**
@@ -249,6 +250,9 @@
EXPECT_EQ(1, listener->updates.size());
EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
+ FieldValue stateFieldValue;
+ mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue);
+ EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value);
listener->updates.clear();
std::unique_ptr<LogEvent> event2 =
@@ -258,6 +262,8 @@
EXPECT_EQ(1, listener->updates.size());
EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
+ mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue);
+ EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value);
listener->updates.clear();
std::unique_ptr<LogEvent> event3 =
@@ -265,8 +271,12 @@
BleScanStateChanged::RESET, false, false, false);
mgr.onLogEvent(*event3);
EXPECT_EQ(2, listener->updates.size());
- EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[0].mState);
- EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[1].mState);
+ for (const TestStateListener::Update& update : listener->updates) {
+ EXPECT_EQ(BleScanStateChanged::OFF, update.mState);
+
+ mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, update.mKey, &stateFieldValue);
+ EXPECT_EQ(BleScanStateChanged::OFF, stateFieldValue.mValue.int_value);
+ }
}
/**
@@ -352,13 +362,13 @@
// No state stored for this query key.
HashableDimensionKey queryKey2;
getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2);
- EXPECT_EQ(WakelockStateChanged::RELEASE,
+ EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/,
getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2));
// Partial query fails.
HashableDimensionKey queryKey3;
getPartialWakelockKey(1001 /* uid */, &queryKey3);
- EXPECT_EQ(WakelockStateChanged::RELEASE,
+ EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/,
getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3));
}
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 687014f..7216e1d 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -569,6 +569,8 @@
AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, state);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -662,9 +664,14 @@
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
writeAttribution(statsEvent, attributionUids, attributionTags);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
AStatsEvent_writeInt32(statsEvent, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
AStatsEvent_writeString(statsEvent, wakelockName.c_str());
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
AStatsEvent_writeInt32(statsEvent, state);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -803,7 +810,11 @@
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
AStatsEvent_writeInt32(statsEvent, state);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -821,10 +832,20 @@
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
writeAttribution(statsEvent, attributionUids, attributionTags);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_writeInt32(statsEvent, filtered); // filtered
- AStatsEvent_writeInt32(statsEvent, firstMatch); // first match
- AStatsEvent_writeInt32(statsEvent, opportunistic); // opportunistic
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true);
+ if (state == util::BLE_SCAN_STATE_CHANGED__STATE__RESET) {
+ AStatsEvent_addInt32Annotation(statsEvent, ANNOTATION_ID_TRIGGER_STATE_RESET,
+ util::BLE_SCAN_STATE_CHANGED__STATE__OFF);
+ }
+ AStatsEvent_writeBool(statsEvent, filtered); // filtered
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_writeBool(statsEvent, firstMatch); // first match
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_writeBool(statsEvent, opportunistic); // opportunistic
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -840,9 +861,14 @@
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
AStatsEvent_writeString(statsEvent, packageName.c_str());
- AStatsEvent_writeInt32(statsEvent, usingAlertWindow);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_writeBool(statsEvent, usingAlertWindow);
AStatsEvent_writeInt32(statsEvent, state);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index f216db6..29a98fa 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -980,16 +980,10 @@
@Override
protected Integer recompute(Void query) {
try {
- mServiceLock.readLock().lock();
- if (mService != null) {
- return mService.getState();
- }
+ return mService.getState();
} catch (RemoteException e) {
- Log.e(TAG, "", e);
- } finally {
- mServiceLock.readLock().unlock();
+ throw e.rethrowFromSystemServer();
}
- return BluetoothAdapter.STATE_OFF;
}
};
@@ -1004,6 +998,30 @@
}
/**
+ * Fetch the current bluetooth state. If the service is down, return
+ * OFF.
+ */
+ @AdapterState
+ private int getStateInternal() {
+ int state = BluetoothAdapter.STATE_OFF;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ state = mBluetoothGetStateCache.query(null);
+ }
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof RemoteException) {
+ Log.e(TAG, "", e.getCause());
+ } else {
+ throw e;
+ }
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return state;
+ }
+
+ /**
* Get the current state of the local Bluetooth adapter.
* <p>Possible return values are
* {@link #STATE_OFF},
@@ -1016,7 +1034,7 @@
@RequiresPermission(Manifest.permission.BLUETOOTH)
@AdapterState
public int getState() {
- int state = mBluetoothGetStateCache.query(null);
+ int state = getStateInternal();
// Consider all internal states as OFF
if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON
@@ -1054,7 +1072,7 @@
@UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine "
+ "whether you can use BLE & BT classic.")
public int getLeState() {
- int state = mBluetoothGetStateCache.query(null);
+ int state = getStateInternal();
if (VDBG) {
Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state));
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 4c95532..2ee0ad6 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -34,6 +34,7 @@
import android.content.pm.parsing.component.ParsedService;
import android.os.Bundle;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import java.security.PublicKey;
import java.util.Map;
@@ -258,6 +259,8 @@
ParsingPackage setManageSpaceActivityName(String manageSpaceActivityName);
+ ParsingPackage setMinExtensionVersions(@Nullable SparseIntArray minExtensionVersions);
+
ParsingPackage setMinSdkVersion(int minSdkVersion);
ParsingPackage setNetworkSecurityConfigRes(int networkSecurityConfigRes);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index be1817d..1a1395c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -51,6 +51,7 @@
import android.util.ArraySet;
import android.util.Pair;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -340,6 +341,8 @@
private String manageSpaceActivityName;
private float maxAspectRatio;
private float minAspectRatio;
+ @Nullable
+ private SparseIntArray minExtensionVersions;
private int minSdkVersion;
private int networkSecurityConfigRes;
@Nullable
@@ -1100,6 +1103,7 @@
dest.writeBoolean(this.preserveLegacyExternalStorage);
dest.writeArraySet(this.mimeGroups);
dest.writeInt(this.gwpAsanMode);
+ dest.writeSparseIntArray(this.minExtensionVersions);
}
public ParsingPackageImpl(Parcel in) {
@@ -1259,6 +1263,7 @@
this.preserveLegacyExternalStorage = in.readBoolean();
this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
this.gwpAsanMode = in.readInt();
+ this.minExtensionVersions = in.readSparseIntArray();
}
public static final Parcelable.Creator<ParsingPackageImpl> CREATOR =
@@ -1767,6 +1772,12 @@
return minAspectRatio;
}
+ @Nullable
+ @Override
+ public SparseIntArray getMinExtensionVersions() {
+ return minExtensionVersions;
+ }
+
@Override
public int getMinSdkVersion() {
return minSdkVersion;
@@ -2215,6 +2226,12 @@
}
@Override
+ public ParsingPackageImpl setMinExtensionVersions(@Nullable SparseIntArray value) {
+ minExtensionVersions = value;
+ return this;
+ }
+
+ @Override
public ParsingPackageImpl setMinSdkVersion(int value) {
minSdkVersion = value;
return this;
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 687bc23..1ded8d4 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -41,6 +41,7 @@
import android.util.ArraySet;
import android.util.Pair;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.android.internal.R;
@@ -609,6 +610,13 @@
String getManageSpaceActivityName();
/**
+ * @see ApplicationInfo#minExtensionVersions
+ * @see R.styleable#AndroidManifestExtensionSdk
+ */
+ @Nullable
+ SparseIntArray getMinExtensionVersions();
+
+ /**
* @see ApplicationInfo#minSdkVersion
* @see R.styleable#AndroidManifestUsesSdk_minSdkVersion
*/
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index c61362f..29ece49 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -95,6 +95,7 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TypedValue;
import android.util.apk.ApkSignatureVerifier;
@@ -1255,6 +1256,7 @@
int type;
final int innerDepth = parser.getDepth();
+ SparseIntArray minExtensionVersions = null;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -1263,7 +1265,10 @@
final ParseResult result;
if (parser.getName().equals("extension-sdk")) {
- result = parseExtensionSdk(input, pkg, res, parser);
+ if (minExtensionVersions == null) {
+ minExtensionVersions = new SparseIntArray();
+ }
+ result = parseExtensionSdk(input, res, parser, minExtensionVersions);
XmlUtils.skipCurrentTag(parser);
} else {
result = ParsingUtils.unknownTag("<uses-sdk>", pkg, parser, input);
@@ -1273,6 +1278,7 @@
return input.error(result);
}
}
+ pkg.setMinExtensionVersions(exactSizedCopyOfSparseArray(minExtensionVersions));
} finally {
sa.recycle();
}
@@ -1280,8 +1286,21 @@
return input.success(pkg);
}
- private static ParseResult parseExtensionSdk(ParseInput input, ParsingPackage pkg,
- Resources res, XmlResourceParser parser) {
+ @Nullable
+ private static SparseIntArray exactSizedCopyOfSparseArray(@Nullable SparseIntArray input) {
+ if (input == null) {
+ return null;
+ }
+ SparseIntArray output = new SparseIntArray(input.size());
+ for (int i = 0; i < input.size(); i++) {
+ output.put(input.keyAt(i), input.valueAt(i));
+ }
+ return output;
+ }
+
+ private static ParseResult<SparseIntArray> parseExtensionSdk(
+ ParseInput input, Resources res, XmlResourceParser parser,
+ SparseIntArray minExtensionVersions) {
int sdkVersion;
int minVersion;
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestExtensionSdk);
@@ -1316,7 +1335,8 @@
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Specified sdkVersion " + sdkVersion + " is not valid");
}
- return input.success(pkg);
+ minExtensionVersions.put(sdkVersion, minVersion);
+ return input.success(minExtensionVersions);
}
/**
diff --git a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
index 882a7f4..b3b4549 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
@@ -233,8 +233,10 @@
Camera.Parameters params = legacyRequest.parameters;
Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
- request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params);
+ ZoomData zoomData = ParameterUtils.convertToLegacyZoom(activeArray,
+ request.get(CaptureRequest.SCALER_CROP_REGION),
+ request.get(CaptureRequest.CONTROL_ZOOM_RATIO),
+ previewSize, params);
List<Face> convertedFaces = new ArrayList<>();
if (faces != null) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 6953a5b..362ddfa 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -771,6 +771,7 @@
CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES ,
CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE ,
CameraCharacteristics.CONTROL_MAX_REGIONS ,
+ CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE ,
CameraCharacteristics.FLASH_INFO_AVAILABLE ,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL ,
CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES ,
@@ -828,6 +829,7 @@
CaptureRequest.CONTROL_MODE,
CaptureRequest.CONTROL_SCENE_MODE,
CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
+ CaptureRequest.CONTROL_ZOOM_RATIO,
CaptureRequest.FLASH_MODE,
CaptureRequest.JPEG_GPS_COORDINATES,
CaptureRequest.JPEG_GPS_PROCESSING_METHOD,
@@ -872,6 +874,7 @@
CaptureResult.CONTROL_AWB_MODE ,
CaptureResult.CONTROL_AWB_LOCK ,
CaptureResult.CONTROL_MODE ,
+ CaptureResult.CONTROL_ZOOM_RATIO ,
CaptureResult.FLASH_MODE ,
CaptureResult.JPEG_GPS_COORDINATES ,
CaptureResult.JPEG_GPS_PROCESSING_METHOD ,
@@ -935,6 +938,12 @@
private static void mapScaler(CameraMetadataNative m, Parameters p) {
/*
+ * control.zoomRatioRange
+ */
+ Range<Float> zoomRatioRange = new Range<Float>(1.0f, ParameterUtils.getMaxZoomRatio(p));
+ m.set(CONTROL_ZOOM_RATIO_RANGE, zoomRatioRange);
+
+ /*
* scaler.availableMaxDigitalZoom
*/
m.set(SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, ParameterUtils.getMaxZoomRatio(p));
@@ -1383,6 +1392,9 @@
// control.sceneMode -- DISABLED is always available
m.set(CaptureRequest.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED);
+ // control.zoomRatio -- 1.0
+ m.set(CaptureRequest.CONTROL_ZOOM_RATIO, 1.0f);
+
/*
* statistics.*
*/
diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
index 2e06d5f..3a46379 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
@@ -68,8 +68,9 @@
*/
ParameterUtils.ZoomData zoomData;
{
- zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
+ zoomData = ParameterUtils.convertToLegacyZoom(activeArray,
request.get(SCALER_CROP_REGION),
+ request.get(CONTROL_ZOOM_RATIO),
previewSize,
params);
diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
index dc5823d..09edf74 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
@@ -118,8 +118,10 @@
Rect activeArraySize = characteristics.get(
CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArraySize,
- request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params);
+ ZoomData zoomData = ParameterUtils.convertToLegacyZoom(activeArraySize,
+ request.get(CaptureRequest.SCALER_CROP_REGION),
+ request.get(CaptureRequest.CONTROL_ZOOM_RATIO),
+ previewSize, params);
/*
* colorCorrection
@@ -516,5 +518,12 @@
{
m.set(SCALER_CROP_REGION, zoomData.reportedCrop);
}
+
+ /*
+ * control.zoomRatio
+ */
+ {
+ m.set(CONTROL_ZOOM_RATIO, zoomData.reportedZoomRatio);
+ }
}
}
diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
index 3cfd020..eb43598 100644
--- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java
+++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
@@ -73,11 +73,15 @@
public final Rect previewCrop;
/** Reported crop-region given the zoom index, coordinates relative to active-array */
public final Rect reportedCrop;
+ /** Reported zoom ratio given the zoom index */
+ public final float reportedZoomRatio;
- public ZoomData(int zoomIndex, Rect previewCrop, Rect reportedCrop) {
+ public ZoomData(int zoomIndex, Rect previewCrop, Rect reportedCrop,
+ float reportedZoomRatio) {
this.zoomIndex = zoomIndex;
this.previewCrop = previewCrop;
this.reportedCrop = reportedCrop;
+ this.reportedZoomRatio = reportedZoomRatio;
}
}
@@ -371,7 +375,8 @@
* @throws NullPointerException if any of the args were {@code null}
*/
public static int getClosestAvailableZoomCrop(
- Camera.Parameters params, Rect activeArray, Size streamSize, Rect cropRegion,
+ Camera.Parameters params, Rect activeArray,
+ Size streamSize, Rect cropRegion,
/*out*/
Rect reportedCropRegion,
Rect previewCropRegion) {
@@ -733,6 +738,92 @@
}
/**
+ * Convert the user-specified crop region/zoom into zoom data; which can be used
+ * to set the parameters to a specific zoom index, or to report back to the user what
+ * the actual zoom was, or for other calculations requiring the current preview crop region.
+ *
+ * <p>None of the parameters are mutated.<p>
+ *
+ * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
+ * @param cropRegion the user-specified crop region
+ * @param zoomRatio the user-specified zoom ratio
+ * @param previewSize the current preview size (in pixels)
+ * @param params the current camera parameters (not mutated)
+ *
+ * @return the zoom index, and the effective/reported crop regions (relative to active array)
+ */
+ public static ZoomData convertToLegacyZoom(Rect activeArraySize, Rect
+ cropRegion, Float zoomRatio, Size previewSize, Camera.Parameters params) {
+ final float FLOAT_EQUAL_THRESHOLD = 0.0001f;
+ if (zoomRatio != null &&
+ Math.abs(1.0f - zoomRatio) > FLOAT_EQUAL_THRESHOLD) {
+ // User uses CONTROL_ZOOM_RATIO to control zoom
+ return convertZoomRatio(activeArraySize, zoomRatio, previewSize, params);
+ }
+
+ return convertScalerCropRegion(activeArraySize, cropRegion, previewSize, params);
+ }
+
+ /**
+ * Convert the user-specified zoom ratio into zoom data; which can be used
+ * to set the parameters to a specific zoom index, or to report back to the user what the
+ * actual zoom was, or for other calculations requiring the current preview crop region.
+ *
+ * <p>None of the parameters are mutated.</p>
+ *
+ * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
+ * @param zoomRatio the current zoom ratio
+ * @param previewSize the current preview size (in pixels)
+ * @param params the current camera parameters (not mutated)
+ *
+ * @return the zoom index, and the effective/reported crop regions (relative to active array)
+ */
+ public static ZoomData convertZoomRatio(Rect activeArraySize, float zoomRatio,
+ Size previewSize, Camera.Parameters params) {
+ if (DEBUG) {
+ Log.v(TAG, "convertZoomRatio - user zoom ratio was " + zoomRatio);
+ }
+
+ List<Rect> availableReportedCropRegions =
+ getAvailableZoomCropRectangles(params, activeArraySize);
+ List<Rect> availablePreviewCropRegions =
+ getAvailablePreviewZoomCropRectangles(params, activeArraySize, previewSize);
+ if (availableReportedCropRegions.size() != availablePreviewCropRegions.size()) {
+ throw new AssertionError("available reported/preview crop region size mismatch");
+ }
+
+ // Find the best matched legacy zoom ratio for the requested camera2 zoom ratio.
+ int bestZoomIndex = 0;
+ Rect reportedCropRegion = new Rect(availableReportedCropRegions.get(0));
+ Rect previewCropRegion = new Rect(availablePreviewCropRegions.get(0));
+ float reportedZoomRatio = 1.0f;
+ if (params.isZoomSupported()) {
+ List<Integer> zoomRatios = params.getZoomRatios();
+ for (int i = 1; i < zoomRatios.size(); i++) {
+ if (zoomRatio * ZOOM_RATIO_MULTIPLIER >= zoomRatios.get(i)) {
+ bestZoomIndex = i;
+ reportedCropRegion = availableReportedCropRegions.get(i);
+ previewCropRegion = availablePreviewCropRegions.get(i);
+ reportedZoomRatio = zoomRatios.get(i);
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (DEBUG) {
+ Log.v(TAG, "convertZoomRatio - zoom calculated to: " +
+ "zoomIndex = " + bestZoomIndex +
+ ", reported crop region = " + reportedCropRegion +
+ ", preview crop region = " + previewCropRegion +
+ ", reported zoom ratio = " + reportedZoomRatio);
+ }
+
+ return new ZoomData(bestZoomIndex, reportedCropRegion,
+ previewCropRegion, reportedZoomRatio);
+ }
+
+ /**
* Convert the user-specified crop region into zoom data; which can be used
* to set the parameters to a specific zoom index, or to report back to the user what the
* actual zoom was, or for other calculations requiring the current preview crop region.
@@ -767,15 +858,17 @@
final int zoomIdx = ParameterUtils.getClosestAvailableZoomCrop(params, activeArraySizeOnly,
previewSize, userCropRegion,
/*out*/reportedCropRegion, /*out*/previewCropRegion);
+ final float reportedZoomRatio = 1.0f;
if (DEBUG) {
Log.v(TAG, "convertScalerCropRegion - zoom calculated to: " +
"zoomIndex = " + zoomIdx +
", reported crop region = " + reportedCropRegion +
- ", preview crop region = " + previewCropRegion);
+ ", preview crop region = " + previewCropRegion +
+ ", reported zoom ratio = " + reportedZoomRatio);
}
- return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion);
+ return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion, reportedZoomRatio);
}
/**
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index 63b3804..9cf1b87 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Size;
import android.app.slice.Slice;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.widget.inline.InlinePresentationSpec;
@@ -67,18 +66,6 @@
return hints.toArray(new String[hints.size()]);
}
- /**
- * @hide
- * @removed
- */
- @UnsupportedAppUsage
- public InlinePresentation(
- @NonNull Slice slice,
- @NonNull android.view.inline.InlinePresentationSpec inlinePresentationSpec,
- boolean pinned) {
- this(slice, inlinePresentationSpec.toWidget(), pinned);
- }
-
// Code below generated by codegen v1.0.15.
@@ -245,7 +232,7 @@
};
@DataClass.Generated(
- time = 1585633564226L,
+ time = 1586992400667L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index dbbe4b6..d6d9fc6 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -123,7 +123,7 @@
}
// Update mNextServedView when focusedView changed.
final View viewForWindowFocus = focusedView != null ? focusedView : mViewRootImpl.mView;
- onViewFocusChanged(viewForWindowFocus, true);
+ onViewFocusChanged(viewForWindowFocus, viewForWindowFocus.hasFocus());
immDelegate.startInputAsyncOnWindowFocusGain(viewForWindowFocus,
windowAttribute.softInputMode, windowAttribute.flags, forceFocus);
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index a9f3e04..0c50cb7 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -17,7 +17,6 @@
package android.view;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
import android.app.Notification;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -27,7 +26,6 @@
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.util.ArraySet;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -396,6 +394,7 @@
addRectAroundView(mIcon);
mExpandButtonRect = addRectAroundView(mExpandButton);
mAppOpsRect = addRectAroundView(mAppOps);
+ setTouchDelegate(new TouchDelegate(mAppOpsRect, mAppOps));
addWidthRect();
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 83a7934..6d3dbfe 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1258,13 +1258,6 @@
}
}
- if (mForAugmentedAutofillOnly) {
- if (sVerbose) {
- Log.v(TAG, "notifyValueChanged(): not notifying system server on "
- + "augmented-only mode");
- }
- return;
- }
if (!mEnabled || !isActiveLocked()) {
if (!startAutofillIfNeededLocked(view)) {
if (sVerbose) {
@@ -1299,10 +1292,6 @@
return;
}
synchronized (mLock) {
- if (mForAugmentedAutofillOnly) {
- if (sVerbose) Log.v(TAG, "notifyValueChanged(): ignoring on augmented only mode");
- return;
- }
if (!mEnabled || !isActiveLocked()) {
if (sVerbose) {
Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
diff --git a/core/java/android/view/inline/InlineContentView.java b/core/java/android/view/inline/InlineContentView.java
deleted file mode 100644
index 3df201c..0000000
--- a/core/java/android/view/inline/InlineContentView.java
+++ /dev/null
@@ -1,208 +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 android.view.inline;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.util.AttributeSet;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.ViewGroup;
-
-/**
- * This class represents a view that holds opaque content from another app that
- * you can inline in your UI.
- *
- * <p>Since the content presented by this view is from another security domain,it is
- * shown on a remote surface preventing the host application from accessing that content.
- * Also the host application cannot interact with the inlined content by injecting touch
- * events or clicking programmatically.
- *
- * <p>This view can be overlaid by other windows, i.e. redressed, but if this is the case
- * the inined UI would not be interactive. Sometimes this is desirable, e.g. animating
- * transitions.
- *
- * <p>By default the surface backing this view is shown on top of the hosting window such
- * that the inlined content is interactive. However, you can temporarily move the surface
- * under the hosting window which could be useful in some cases, e.g. animating transitions.
- * At this point the inlined content will not be interactive and the touch events would
- * be delivered to your app.
- *
- * @hide
- * @removed
- */
-public class InlineContentView extends ViewGroup {
-
- /**
- * Callback for observing the lifecycle of the surface control
- * that manipulates the backing secure embedded UI surface.
- */
- public interface SurfaceControlCallback {
- /**
- * Called when the backing surface is being created.
- *
- * @param surfaceControl The surface control to manipulate the surface.
- */
- void onCreated(@NonNull SurfaceControl surfaceControl);
-
- /**
- * Called when the backing surface is being destroyed.
- *
- * @param surfaceControl The surface control to manipulate the surface.
- */
- void onDestroyed(@NonNull SurfaceControl surfaceControl);
- }
-
- private final @NonNull SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
- @Override
- public void surfaceCreated(@NonNull SurfaceHolder holder) {
- mSurfaceControlCallback.onCreated(mSurfaceView.getSurfaceControl());
- }
-
- @Override
- public void surfaceChanged(@NonNull SurfaceHolder holder,
- int format, int width, int height) {
- /* do nothing */
- }
-
- @Override
- public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
- mSurfaceControlCallback.onDestroyed(mSurfaceView.getSurfaceControl());
- }
- };
-
- private final @NonNull SurfaceView mSurfaceView;
-
- private @Nullable SurfaceControlCallback mSurfaceControlCallback;
-
- /**
- * @inheritDoc
- *
- * @hide
- */
- public InlineContentView(@NonNull Context context) {
- this(context, null);
- }
-
- /**
- * @inheritDoc
- *
- * @hide
- */
- public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- /**
- * @inheritDoc
- *
- * @hide
- */
- public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- /**
- * Gets the surface control. If the surface is not created this method
- * returns {@code null}.
- *
- * @return The surface control.
- *
- * @see #setSurfaceControlCallback(SurfaceControlCallback)
- */
- public @Nullable SurfaceControl getSurfaceControl() {
- return mSurfaceView.getSurfaceControl();
- }
-
- /**
- * @inheritDoc
- *
- * @hide
- */
- public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- mSurfaceView = new SurfaceView(context, attrs, defStyleAttr, defStyleRes);
- mSurfaceView.setZOrderOnTop(true);
- mSurfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);
- addView(mSurfaceView);
- }
-
- /**
- * Sets the embedded UI.
- * @param surfacePackage The embedded UI.
- *
- * @hide
- */
- public void setChildSurfacePackage(
- @Nullable SurfaceControlViewHost.SurfacePackage surfacePackage) {
- mSurfaceView.setChildSurfacePackage(surfacePackage);
- }
-
- @Override
- public void onLayout(boolean changed, int l, int t, int r, int b) {
- mSurfaceView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
- }
-
- /**
- * Sets a callback to observe the lifecycle of the surface control for
- * managing the backing surface.
- *
- * @param callback The callback to set or {@code null} to clear.
- */
- public void setSurfaceControlCallback(@Nullable SurfaceControlCallback callback) {
- if (mSurfaceControlCallback != null) {
- mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
- }
- mSurfaceControlCallback = callback;
- if (mSurfaceControlCallback != null) {
- mSurfaceView.getHolder().addCallback(mSurfaceCallback);
- }
- }
-
- /**
- * @return Whether the surface backing this view appears on top of its parent.
- *
- * @see #setZOrderedOnTop(boolean)
- */
- public boolean isZOrderedOnTop() {
- return mSurfaceView.isZOrderedOnTop();
- }
-
- /**
- * Controls whether the backing surface is placed on top of this view's window.
- * Normally, it is placed on top of the window, to allow interaction
- * with the inlined UI. Via this method, you can place the surface below the
- * window. This means that all of the contents of the window this view is in
- * will be visible on top of its surface.
- *
- * <p> The Z ordering can be changed dynamically if the backing surface is
- * created, otherwise the ordering would be applied at surface construction time.
- *
- * @param onTop Whether to show the surface on top of this view's window.
- *
- * @see #isZOrderedOnTop()
- */
- public boolean setZOrderedOnTop(boolean onTop) {
- return mSurfaceView.setZOrderedOnTop(onTop, /*allowDynamicChange*/ true);
- }
-}
diff --git a/core/java/android/view/inline/InlinePresentationSpec.aidl b/core/java/android/view/inline/InlinePresentationSpec.aidl
deleted file mode 100644
index 680ee4e..0000000
--- a/core/java/android/view/inline/InlinePresentationSpec.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.inline;
-
-/**
- * @hide
- * @removed
- */
-parcelable InlinePresentationSpec;
diff --git a/core/java/android/view/inline/InlinePresentationSpec.java b/core/java/android/view/inline/InlinePresentationSpec.java
deleted file mode 100644
index d777cb8..0000000
--- a/core/java/android/view/inline/InlinePresentationSpec.java
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.inline;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.Size;
-
-import com.android.internal.util.DataClass;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class represents the presentation specification by which an inline suggestion
- * should abide when constructing its UI. Since suggestions are inlined in a
- * host application while provided by another source, they need to be consistent
- * with the host's look at feel to allow building smooth and integrated UIs.
- *
- * @hide
- * @removed
- */
-@DataClass(genEqualsHashCode = true, genToString = true, genBuilder = true)
-public final class InlinePresentationSpec implements Parcelable {
-
- /** The minimal size of the suggestion. */
- @NonNull
- private final Size mMinSize;
- /** The maximal size of the suggestion. */
- @NonNull
- private final Size mMaxSize;
-
- /**
- * The extras encoding the UI style information. Defaults to {@code Bundle.EMPTY} in which case
- * the default system UI style will be used.
- */
- @NonNull
- private final Bundle mStyle;
-
- private static Bundle defaultStyle() {
- return Bundle.EMPTY;
- }
-
- /** @hide */
- @DataClass.Suppress({"setMaxSize", "setMinSize"})
- abstract static class BaseBuilder {
- }
-
- /**
- * @hide
- */
- public android.widget.inline.InlinePresentationSpec toWidget() {
- final android.widget.inline.InlinePresentationSpec.Builder builder =
- new android.widget.inline.InlinePresentationSpec.Builder(
- getMinSize(), getMaxSize());
- final Bundle style = getStyle();
- if (style != null) {
- builder.setStyle(style);
- }
- return builder.build();
- }
-
- /**
- * @hide
- */
- public static android.view.inline.InlinePresentationSpec fromWidget(
- android.widget.inline.InlinePresentationSpec widget) {
- final android.view.inline.InlinePresentationSpec.Builder builder =
- new android.view.inline.InlinePresentationSpec.Builder(
- widget.getMinSize(), widget.getMaxSize());
- final Bundle style = widget.getStyle();
- if (style != null) {
- builder.setStyle(style);
- }
- return builder.build();
- }
-
- /**
- * @hide
- */
- public static List<android.view.inline.InlinePresentationSpec> fromWidgets(
- List<android.widget.inline.InlinePresentationSpec> widgets) {
- final ArrayList<android.view.inline.InlinePresentationSpec> convertedSpecs =
- new ArrayList<>();
- for (int i = 0; i < widgets.size(); i++) {
- convertedSpecs.add(fromWidget(widgets.get(i)));
- }
- return convertedSpecs;
- }
-
-
-
- // Code below generated by codegen v1.0.15.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- @DataClass.Generated.Member
- /* package-private */ InlinePresentationSpec(
- @NonNull Size minSize,
- @NonNull Size maxSize,
- @NonNull Bundle style) {
- this.mMinSize = minSize;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mMinSize);
- this.mMaxSize = maxSize;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mMaxSize);
- this.mStyle = style;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mStyle);
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- /**
- * The minimal size of the suggestion.
- */
- @UnsupportedAppUsage
- @DataClass.Generated.Member
- public @NonNull Size getMinSize() {
- return mMinSize;
- }
-
- /**
- * The maximal size of the suggestion.
- */
- @UnsupportedAppUsage
- @DataClass.Generated.Member
- public @NonNull Size getMaxSize() {
- return mMaxSize;
- }
-
- /**
- * The extras encoding the UI style information. Defaults to {@code Bundle.EMPTY} in which case
- * the default system UI style will be used.
- */
- @DataClass.Generated.Member
- public @NonNull Bundle getStyle() {
- return mStyle;
- }
-
- @Override
- @DataClass.Generated.Member
- public String toString() {
- // You can override field toString logic by defining methods like:
- // String fieldNameToString() { ... }
-
- return "InlinePresentationSpec { " +
- "minSize = " + mMinSize + ", " +
- "maxSize = " + mMaxSize + ", " +
- "style = " + mStyle +
- " }";
- }
-
- @Override
- @DataClass.Generated.Member
- public boolean equals(@Nullable Object o) {
- // You can override field equality logic by defining either of the methods like:
- // boolean fieldNameEquals(InlinePresentationSpec other) { ... }
- // boolean fieldNameEquals(FieldType otherValue) { ... }
-
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- @SuppressWarnings("unchecked")
- InlinePresentationSpec that = (InlinePresentationSpec) o;
- //noinspection PointlessBooleanExpression
- return true
- && java.util.Objects.equals(mMinSize, that.mMinSize)
- && java.util.Objects.equals(mMaxSize, that.mMaxSize)
- && java.util.Objects.equals(mStyle, that.mStyle);
- }
-
- @Override
- @DataClass.Generated.Member
- public int hashCode() {
- // You can override field hashCode logic by defining methods like:
- // int fieldNameHashCode() { ... }
-
- int _hash = 1;
- _hash = 31 * _hash + java.util.Objects.hashCode(mMinSize);
- _hash = 31 * _hash + java.util.Objects.hashCode(mMaxSize);
- _hash = 31 * _hash + java.util.Objects.hashCode(mStyle);
- return _hash;
- }
-
- @Override
- @DataClass.Generated.Member
- public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
- // You can override field parcelling by defining methods like:
- // void parcelFieldName(Parcel dest, int flags) { ... }
-
- dest.writeSize(mMinSize);
- dest.writeSize(mMaxSize);
- dest.writeBundle(mStyle);
- }
-
- @Override
- @DataClass.Generated.Member
- public int describeContents() { return 0; }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- /* package-private */ InlinePresentationSpec(@NonNull android.os.Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- Size minSize = (Size) in.readSize();
- Size maxSize = (Size) in.readSize();
- Bundle style = in.readBundle();
-
- this.mMinSize = minSize;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mMinSize);
- this.mMaxSize = maxSize;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mMaxSize);
- this.mStyle = style;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mStyle);
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- @DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<InlinePresentationSpec> CREATOR
- = new Parcelable.Creator<InlinePresentationSpec>() {
- @Override
- public InlinePresentationSpec[] newArray(int size) {
- return new InlinePresentationSpec[size];
- }
-
- @Override
- public InlinePresentationSpec createFromParcel(@NonNull android.os.Parcel in) {
- return new InlinePresentationSpec(in);
- }
- };
-
- /**
- * A builder for {@link InlinePresentationSpec}
- */
- @SuppressWarnings("WeakerAccess")
- @DataClass.Generated.Member
- public static final class Builder extends BaseBuilder {
-
- private @NonNull Size mMinSize;
- private @NonNull Size mMaxSize;
- private @NonNull Bundle mStyle;
-
- private long mBuilderFieldsSet = 0L;
-
- /**
- * Creates a new Builder.
- *
- * @param minSize
- * The minimal size of the suggestion.
- * @param maxSize
- * The maximal size of the suggestion.
- */
- @UnsupportedAppUsage
- public Builder(
- @NonNull Size minSize,
- @NonNull Size maxSize) {
- mMinSize = minSize;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mMinSize);
- mMaxSize = maxSize;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mMaxSize);
- }
-
- /**
- * The extras encoding the UI style information. Defaults to {@code Bundle.EMPTY} in which case
- * the default system UI style will be used.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setStyle(@NonNull Bundle value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x4;
- mStyle = value;
- return this;
- }
-
- /** Builds the instance. This builder should not be touched after calling this! */
- @UnsupportedAppUsage
- @NonNull
- public InlinePresentationSpec build() {
- checkNotUsed();
- mBuilderFieldsSet |= 0x8; // Mark builder used
-
- if ((mBuilderFieldsSet & 0x4) == 0) {
- mStyle = defaultStyle();
- }
- InlinePresentationSpec o = new InlinePresentationSpec(
- mMinSize,
- mMaxSize,
- mStyle);
- return o;
- }
-
- private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x8) != 0) {
- throw new IllegalStateException(
- "This Builder should not be reused. Use a new Builder instance instead");
- }
- }
- }
-
- @DataClass.Generated(
- time = 1585691139012L,
- codegenVersion = "1.0.15",
- sourceFile = "frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java",
- inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.NonNull android.os.Bundle mStyle\nprivate static android.os.Bundle defaultStyle()\npublic android.widget.inline.InlinePresentationSpec toWidget()\npublic static android.view.inline.InlinePresentationSpec fromWidget(android.widget.inline.InlinePresentationSpec)\npublic static java.util.List<android.view.inline.InlinePresentationSpec> fromWidgets(java.util.List<android.widget.inline.InlinePresentationSpec>)\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
-}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 3e9ffa7..1c703ec 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -20,7 +20,6 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcelable;
import android.widget.inline.InlinePresentationSpec;
@@ -87,17 +86,6 @@
return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned);
}
- /**
- * The presentation spec to which the inflated suggestion view abides.
- *
- * @hide
- * @removed
- */
- @UnsupportedAppUsage
- public @NonNull android.view.inline.InlinePresentationSpec getPresentationSpec() {
- return android.view.inline.InlinePresentationSpec.fromWidget(mInlinePresentationSpec);
- }
-
// Code below generated by codegen v1.0.15.
@@ -358,10 +346,10 @@
};
@DataClass.Generated(
- time = 1585633580662L,
+ time = 1586992414034L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionInfo.java",
- inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.view.inline.InlinePresentationSpec getPresentationSpec()\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+ inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index af896fc..d282b56 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.IBinder;
import android.os.LocaleList;
@@ -93,20 +92,6 @@
private int mHostDisplayId;
/**
- * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
- * count is larger than the number of specs in the list, then the last spec is used for the
- * remainder of the suggestions. The list should not be empty.
- *
- * @hide
- * @removed
- */
- @UnsupportedAppUsage
- @NonNull
- public List<android.view.inline.InlinePresentationSpec> getPresentationSpecs() {
- return android.view.inline.InlinePresentationSpec.fromWidgets(mInlinePresentationSpecs);
- }
-
- /**
* @hide
* @see {@link #mHostInputToken}.
*/
@@ -170,17 +155,6 @@
/** @hide */
abstract static class BaseBuilder {
- /**
- * @hide
- * @removed
- */
- @UnsupportedAppUsage
- @NonNull
- public Builder addPresentationSpecs(
- @NonNull android.view.inline.InlinePresentationSpec value) {
- return ((Builder) this).addInlinePresentationSpecs(value.toWidget());
- }
-
abstract Builder setInlinePresentationSpecs(
@NonNull List<android.widget.inline.InlinePresentationSpec> specs);
@@ -608,10 +582,10 @@
}
@DataClass.Generated(
- time = 1585768018462L,
+ time = 1586992395497L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> getPresentationSpecs()\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.view.inputmethod.InlineSuggestionsRequest.Builder addPresentationSpecs(android.view.inline.InlinePresentationSpec)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 62dd192..51d37a5 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -5440,6 +5440,9 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
+ if (!mTextView.isFromPrimePointer(ev, true)) {
+ return true;
+ }
if (mFlagInsertionHandleGesturesEnabled && mFlagCursorDragFromAnywhereEnabled) {
// Should only enable touch through when cursor drag is enabled.
// Otherwise the insertion handle view cannot be moved.
@@ -5908,6 +5911,9 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (!mTextView.isFromPrimePointer(event, true)) {
+ return true;
+ }
boolean superResult = super.onTouchEvent(event);
switch (event.getActionMasked()) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e178318..4be9e1a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -855,6 +855,19 @@
int mTextEditSuggestionContainerLayout;
int mTextEditSuggestionHighlightStyle;
+ private static final int NO_POINTER_ID = -1;
+ /**
+ * The prime (the 1st finger) pointer id which is used as a lock to prevent multi touch among
+ * TextView and the handle views which are rendered on popup windows.
+ */
+ private int mPrimePointerId = NO_POINTER_ID;
+
+ /**
+ * Whether the prime pointer is from the event delivered to selection handle or insertion
+ * handle.
+ */
+ private boolean mIsPrimePointerFromHandleView;
+
/**
* {@link EditText} specific data, created on demand when one of the Editor fields is used.
* See {@link #createEditorIfNeeded()}.
@@ -10886,6 +10899,36 @@
}
}
+ /**
+ * Called from onTouchEvent() to prevent the touches by secondary fingers.
+ * Dragging on handles can revise cursor/selection, so can dragging on the text view.
+ * This method is a lock to avoid processing multiple fingers on both text view and handles.
+ * Note: multiple fingers on handles (e.g. 2 fingers on the 2 selection handles) should work.
+ *
+ * @param event The motion event that is being handled and carries the pointer info.
+ * @param fromHandleView true if the event is delivered to selection handle or insertion
+ * handle; false if this event is delivered to TextView.
+ * @return Returns true to indicate that onTouchEvent() can continue processing the motion
+ * event, otherwise false.
+ * - Always returns true for the first finger.
+ * - For secondary fingers, if the first or current finger is from TextView, returns false.
+ * This is to make touch mutually exclusive between the TextView and the handles, but
+ * not among the handles.
+ */
+ boolean isFromPrimePointer(MotionEvent event, boolean fromHandleView) {
+ if (mPrimePointerId == NO_POINTER_ID) {
+ mPrimePointerId = event.getPointerId(0);
+ mIsPrimePointerFromHandleView = fromHandleView;
+ } else if (mPrimePointerId != event.getPointerId(0)) {
+ return mIsPrimePointerFromHandleView && fromHandleView;
+ }
+ if (event.getActionMasked() == MotionEvent.ACTION_UP
+ || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
+ mPrimePointerId = -1;
+ }
+ return true;
+ }
+
@Override
public boolean onTouchEvent(MotionEvent event) {
if (DEBUG_CURSOR) {
@@ -10894,6 +10937,9 @@
MotionEvent.actionToString(event.getActionMasked()),
event.getX(), event.getY());
}
+ if (!isFromPrimePointer(event, false)) {
+ return true;
+ }
final int action = event.getActionMasked();
if (mEditor != null) {
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index d64b5f1..be66d0c 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -232,9 +232,8 @@
}
/**
- * Show toast if current assigned shortcut target is an accessibility service and its target
- * sdk version is less than or equal to Q, or greater than Q and does not request
- * accessibility button.
+ * Show toast to alert the user that the accessibility shortcut turned on or off an
+ * accessibility service.
*/
private void showToast() {
final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
@@ -247,12 +246,15 @@
}
final boolean requestA11yButton = (serviceInfo.flags
& AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
- if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo
- .targetSdkVersion > Build.VERSION_CODES.Q && requestA11yButton) {
+ final boolean isServiceEnabled = isServiceEnabled(serviceInfo);
+ if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion
+ > Build.VERSION_CODES.Q && requestA11yButton && isServiceEnabled) {
+ // An accessibility button callback is sent to the target accessibility service.
+ // No need to show up a toast in this case.
return;
}
// For accessibility services, show a toast explaining what we're doing.
- String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
+ String toastMessageFormatString = mContext.getString(isServiceEnabled
? R.string.accessibility_shortcut_disabling_service
: R.string.accessibility_shortcut_enabling_service);
String toastMessage = String.format(toastMessageFormatString, serviceName);
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index d43333e..b81e473 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -429,16 +429,16 @@
subtitle.setVisibility(View.GONE);
}
- ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon);
Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button);
+ button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
+ button.setOnClickListener(buttonOnClick);
+
+ ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon);
if (!getContext().getResources().getBoolean(R.bool.resolver_landscape_phone)) {
icon.setVisibility(View.VISIBLE);
icon.setImageResource(iconRes);
- button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
- button.setOnClickListener(buttonOnClick);
} else {
icon.setVisibility(View.GONE);
- button.setVisibility(View.GONE);
}
activeListAdapter.markTabLoaded();
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index eda04a6..ea41618 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -16,14 +16,39 @@
package com.android.internal.app.procstats;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_NOTHING;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_MOD;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_OFF;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_ON;
+import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_HOME;
+import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND;
+import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_FOREGROUND;
+import static com.android.internal.app.procstats.ProcessStats.STATE_LAST_ACTIVITY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
+import static com.android.internal.app.procstats.ProcessStats.STATE_PERSISTENT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_RECEIVER;
+import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE;
+import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE_RESTARTING;
+import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
+
import android.os.UserHandle;
import android.service.procstats.ProcessStatsEnums;
import android.service.procstats.ProcessStatsStateProto;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
-import static com.android.internal.app.procstats.ProcessStats.*;
-
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -38,6 +63,7 @@
public static final String[] STATE_NAMES_CSV;
static final String[] STATE_TAGS;
static final int[] STATE_PROTO_ENUMS;
+ private static final int[] PROCESS_STATS_STATE_TO_AGGREGATED_STATE;
// Make the mapping easy to update.
static {
@@ -126,6 +152,39 @@
STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY_CLIENT] =
ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
STATE_PROTO_ENUMS[STATE_CACHED_EMPTY] = ProcessStatsEnums.PROCESS_STATE_CACHED_EMPTY;
+
+ // Remap states, as defined by ProcessStats.java, to a reduced subset of states for data
+ // aggregation / size reduction purposes.
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE = new int[STATE_COUNT];
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_PERSISTENT] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_PERSISTENT;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_TOP] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_TOP;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_IMPORTANT_FOREGROUND] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_IMPORTANT_FOREGROUND;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_IMPORTANT_BACKGROUND] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BACKUP] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_SERVICE] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+ // "Restarting" is not a real state, so this shouldn't exist.
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_SERVICE_RESTARTING] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_UNKNOWN;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_RECEIVER] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_RECEIVER;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_HEAVY_WEIGHT] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_HOME] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_LAST_ACTIVITY] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY_CLIENT] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_EMPTY] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
}
public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
@@ -455,4 +514,29 @@
}
return itemName;
}
+
+ /**
+ * Aggregate process states to reduce size of statistics logs.
+ *
+ * <p>Involves unpacking the three parts of state (process state / device memory state /
+ * screen state), manipulating the elements, then re-packing the new values into a single
+ * int. This integer is guaranteed to be unique for any given combination of state elements.
+ *
+ * @param curState current state as used in mCurState in {@class ProcessState} ie. a value
+ * combined from the process's state, the device's memory pressure state, and
+ * the device's screen on/off state.
+ * @return an integer representing the combination of screen state and process state, where
+ * process state has been aggregated.
+ */
+ public static int aggregateCurrentProcessState(int curState) {
+ int screenStateIndex = curState / (ADJ_SCREEN_MOD * STATE_COUNT);
+ // extract process state from the compound state variable (discarding memory state)
+ int procStateIndex = curState % STATE_COUNT;
+
+ // Remap process state per array above.
+ procStateIndex = PROCESS_STATS_STATE_TO_AGGREGATED_STATE[procStateIndex];
+
+ // Pack screen & process state using bit shifting
+ return (procStateIndex << 8) | screenStateIndex;
+ }
}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 608925578..55ea3159 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -250,7 +250,7 @@
reserved 3; // activity
optional bool fills_parent = 4;
optional .android.graphics.RectProto bounds = 5;
- optional .android.graphics.RectProto displayed_bounds = 6;
+ optional .android.graphics.RectProto displayed_bounds = 6 [deprecated=true];
optional bool defer_removal = 7;
optional int32 surface_width = 8;
optional int32 surface_height = 9;
diff --git a/core/proto/android/service/procstats_enum.proto b/core/proto/android/service/procstats_enum.proto
index cc3fe5a..2abf373 100644
--- a/core/proto/android/service/procstats_enum.proto
+++ b/core/proto/android/service/procstats_enum.proto
@@ -76,3 +76,27 @@
SERVICE_OPERATION_STATE_BOUND = 4;
SERVICE_OPERATION_STATE_EXECUTING = 5;
}
+
+// this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
+// and not frameworks/base/core/java/android/app/ActivityManager.java
+enum AggregatedProcessState {
+ AGGREGATED_PROCESS_STATE_UNKNOWN = 0;
+ // Persistent system process; PERSISTENT or PERSISTENT_UI in ActivityManager
+ AGGREGATED_PROCESS_STATE_PERSISTENT = 1;
+ // Top activity; actually any visible activity; TOP or TOP_SLEEPING in ActivityManager
+ AGGREGATED_PROCESS_STATE_TOP = 2;
+ // Bound top foreground process; BOUND_TOP or BOUND_FOREGROUND_SERVICE in ActivityManager
+ AGGREGATED_PROCESS_STATE_BOUND_TOP_OR_FGS = 3;
+ // Important foreground process; FOREGROUND_SERVICE in ActivityManager
+ AGGREGATED_PROCESS_STATE_FGS = 4;
+ // Important foreground process ; IMPORTANT_FOREGROUND in ActivityManager
+ AGGREGATED_PROCESS_STATE_IMPORTANT_FOREGROUND = 5;
+ // Various background processes; IMPORTANT_BACKGROUND, TRANSIENT_BACKGROUND, BACKUP, SERVICE,
+ // HEAVY_WEIGHT in ActivityManager
+ AGGREGATED_PROCESS_STATE_BACKGROUND = 6;
+ // Process running a receiver; RECEIVER in ActivityManager
+ AGGREGATED_PROCESS_STATE_RECEIVER = 7;
+ // Various cached processes; HOME, LAST_ACTIVITY, CACHED_ACTIVITY, CACHED_RECENT,
+ // CACHED_ACTIVITY_CLIENT, CACHED_EMPTY in ActivityManager
+ AGGREGATED_PROCESS_STATE_CACHED = 8;
+}
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 6f36aae..ece59e2 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -143,34 +143,35 @@
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_marginStart="6dp"
- android:orientation="horizontal" >
- <ImageButton
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:orientation="horizontal">
+ <ImageView
android:id="@+id/camera"
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_camera"
- android:background="?android:selectableItemBackgroundBorderless"
android:visibility="gone"
+ android:focusable="false"
android:contentDescription="@string/notification_appops_camera_active"
/>
- <ImageButton
+ <ImageView
android:id="@+id/mic"
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_mic"
- android:background="?android:selectableItemBackgroundBorderless"
android:layout_marginStart="4dp"
android:visibility="gone"
+ android:focusable="false"
android:contentDescription="@string/notification_appops_microphone_active"
/>
- <ImageButton
+ <ImageView
android:id="@+id/overlay"
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_alert_window_layer"
- android:background="?android:selectableItemBackgroundBorderless"
android:layout_marginStart="4dp"
android:visibility="gone"
+ android:focusable="false"
android:contentDescription="@string/notification_appops_overlay_active"
/>
</LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index e986b18..8a57e05 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -195,35 +195,36 @@
android:layout_width="wrap_content"
android:paddingTop="3dp"
android:layout_marginStart="2dp"
+ android:background="?android:selectableItemBackgroundBorderless"
android:orientation="horizontal" >
- <ImageButton
+ <ImageView
android:layout_marginStart="4dp"
android:id="@+id/camera"
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_camera"
- android:background="?android:selectableItemBackgroundBorderless"
android:visibility="gone"
+ android:focusable="false"
android:contentDescription="@string/notification_appops_camera_active"
/>
- <ImageButton
+ <ImageView
android:id="@+id/mic"
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_mic"
- android:background="?android:selectableItemBackgroundBorderless"
android:layout_marginStart="4dp"
android:visibility="gone"
+ android:focusable="false"
android:contentDescription="@string/notification_appops_microphone_active"
/>
- <ImageButton
+ <ImageView
android:id="@+id/overlay"
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_alert_window_layer"
- android:background="?android:selectableItemBackgroundBorderless"
android:layout_marginStart="4dp"
android:visibility="gone"
+ android:focusable="false"
android:contentDescription="@string/notification_appops_overlay_active"
/>
</LinearLayout>
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index a9f251e..89cc6e7 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -528,6 +528,47 @@
}
@Test
+ public void testCursorDrag_multiTouch() throws Throwable {
+ String text = "line1: This is the 1st line: A";
+ onView(withId(R.id.textview)).perform(replaceText(text));
+ TextView tv = mActivity.findViewById(R.id.textview);
+ Editor editor = tv.getEditorForTesting();
+ final int startIndex = text.indexOf("1st line");
+ Layout layout = tv.getLayout();
+ final float cursorStartX =
+ layout.getPrimaryHorizontal(startIndex) + tv.getTotalPaddingLeft();
+ final float cursorStartY = layout.getLineTop(1) + tv.getTotalPaddingTop();
+
+ // Taps to show the insertion handle.
+ tapAtPoint(tv, cursorStartX, cursorStartY);
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex));
+ View handleView = editor.getInsertionController().getHandle();
+
+ // Taps & holds the insertion handle.
+ long handleDownTime = sTicker.addAndGet(10_000);
+ long eventTime = handleDownTime;
+ dispatchTouchEvent(handleView, downEvent(handleView, handleDownTime, eventTime++, 0, 0));
+
+ // Tries to Drag the cursor, with the pointer id > 0 (meaning the 2nd finger).
+ long cursorDownTime = eventTime++;
+ dispatchTouchEvent(tv, obtainTouchEventWithPointerId(
+ tv, MotionEvent.ACTION_DOWN, cursorDownTime, eventTime++, 1,
+ cursorStartX - 50, cursorStartY));
+ dispatchTouchEvent(tv, obtainTouchEventWithPointerId(
+ tv, MotionEvent.ACTION_MOVE, cursorDownTime, eventTime++, 1,
+ cursorStartX - 100, cursorStartY));
+ dispatchTouchEvent(tv, obtainTouchEventWithPointerId(
+ tv, MotionEvent.ACTION_UP, cursorDownTime, eventTime++, 1,
+ cursorStartX - 100, cursorStartY));
+
+ // Checks the cursor drag doesn't work while the handle is being hold.
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex));
+
+ // Finger up on the insertion handle.
+ dispatchTouchEvent(handleView, upEvent(handleView, handleDownTime, eventTime, 0, 0));
+ }
+
+ @Test
public void testCursorDrag_snapDistance() throws Throwable {
String text = "line1: This is the 1st line: A\n"
+ "line2: This is the 2nd line: B\n"
@@ -626,6 +667,24 @@
return event;
}
+ private MotionEvent obtainTouchEventWithPointerId(
+ View view, int action, long downTime, long eventTime, int pointerId, float x, float y) {
+ Rect r = new Rect();
+ view.getBoundsOnScreen(r);
+ float rawX = x + r.left;
+ float rawY = y + r.top;
+ MotionEvent.PointerCoords coordinates = new MotionEvent.PointerCoords();
+ coordinates.x = rawX;
+ coordinates.y = rawY;
+ MotionEvent event = MotionEvent.obtain(
+ downTime, eventTime, action, 1, new int[] {pointerId},
+ new MotionEvent.PointerCoords[] {coordinates},
+ 0, 1f, 1f, 0, 0, 0, 0);
+ view.toLocalMotionEvent(event);
+ mMotionEvents.add(event);
+ return event;
+ }
+
private MotionEvent obtainMouseEvent(
View view, int action, long downTime, long eventTime, float x, float y) {
MotionEvent event = obtainTouchEvent(view, action, downTime, eventTime, x, y);
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 4a33da6..b21504c 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -462,6 +462,7 @@
configureValidShortcutService();
configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
configureRequestAccessibilityButton();
+ configureEnabledService();
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
getController().performAccessibilityShortcut();
@@ -610,6 +611,11 @@
}).when(mHandler).sendMessageAtTime(any(), anyLong());
}
+ private void configureEnabledService() throws Exception {
+ when(mAccessibilityManagerService.getEnabledAccessibilityServiceList(anyInt(), anyInt()))
+ .thenReturn(Collections.singletonList(mServiceInfo));
+ }
+
private AccessibilityShortcutController getController() {
AccessibilityShortcutController accessibilityShortcutController =
new AccessibilityShortcutController(mContext, mHandler, 0);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index c11e1a0..6fbee16 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -30,13 +30,17 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
+import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.UserManager;
import android.permission.IPermissionManager;
import android.util.Log;
+import java.util.List;
+
/**
* Select which activity is the first visible activity of the installation and forward the intent to
* it.
@@ -47,6 +51,7 @@
private static final String DOWNLOADS_AUTHORITY = "downloads";
private IPackageManager mIPackageManager;
private IPermissionManager mIPermissionManager;
+ private UserManager mUserManager;
private boolean mAbortInstall = false;
@Override
@@ -54,6 +59,7 @@
super.onCreate(savedInstanceState);
mIPackageManager = AppGlobals.getPackageManager();
mIPermissionManager = AppGlobals.getPermissionManager();
+ mUserManager = getSystemService(UserManager.class);
Intent intent = getIntent();
String callingPackage = getCallingPackage();
@@ -144,13 +150,16 @@
if (packages == null) {
return false;
}
+ final List<UserInfo> users = mUserManager.getUsers();
for (String packageName : packages) {
- try {
- if (uid == getPackageManager().getPackageUid(packageName, 0)) {
- return true;
+ for (UserInfo user : users) {
+ try {
+ if (uid == getPackageManager().getPackageUidAsUser(packageName, user.id)) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Ignore and try the next package
}
- } catch (PackageManager.NameNotFoundException e) {
- // Ignore and try the next package
}
}
} catch (RemoteException rexc) {
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index 9dc502e..f8a96e7 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -108,7 +108,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
- style="@style/TextAppearance.NotificationImportanceChannel"/>
+ style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
</LinearLayout>
<TextView
android:id="@+id/delegate_name"
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 5b36382..e8e0133 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -84,7 +84,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
- style="@style/TextAppearance.NotificationImportanceChannel"/>
+ style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
</LinearLayout>
<TextView
android:id="@+id/delegate_name"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d2654d6..49420e8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1829,15 +1829,15 @@
<!-- [CHAR LIMIT=150] Notification Importance title: normal importance level summary -->
<string name="notification_channel_summary_default">Gets your attention with sound or vibration.</string>
+ <!-- [CHAR LIMIT=150] Conversation Notification Importance title: normal conversation level, with bubbling summary -->
+ <string name="notification_channel_summary_default_with_bubbles">Gets your attention with sound or vibration. Conversations from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubble by default.</string>
+
<!-- [CHAR LIMIT=150] Notification Importance title: bubble level summary -->
<string name="notification_channel_summary_bubble">Keeps your attention with a floating shortcut to this content.</string>
<!-- [CHAR LIMIT=150] Notification Importance title: important conversation level summary -->
<string name="notification_channel_summary_priority">Shows at top of conversation section and appears as a bubble.</string>
- <!--[CHAR LIMIT=150] Conversation inline controls footer shown when all conversations from the app are allowed to show as bubbles -->
- <string name="notification_conversation_channel_all_bubble">All conversations from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubble by default. Manage in <xliff:g id="app_name" example="Settings">%2$s</xliff:g>.</string>
-
<!--[CHAR LIMIT=30] Linkable text to Settings app -->
<string name="notification_conversation_channel_settings">Settings</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 118aa5b..7e24f5d 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -564,7 +564,7 @@
<style name="TextAppearance.NotificationImportanceButton">
<item name="android:textSize">@dimen/notification_importance_button_text</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- <item name="android:textColor">?android:attr/colorAccent</item>
+ <item name="android:textColor">@color/notification_guts_priority_contents</item>
<item name="android:gravity">center</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 922fb69b..7861211 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -948,7 +948,12 @@
int dw = flipped ? lh : lw;
int dh = flipped ? lw : lh;
- mBoundingPath.set(DisplayCutout.pathFromResources(getResources(), dw, dh));
+ Path path = DisplayCutout.pathFromResources(getResources(), dw, dh);
+ if (path != null) {
+ mBoundingPath.set(path);
+ } else {
+ mBoundingPath.reset();
+ }
Matrix m = new Matrix();
transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m);
mBoundingPath.transform(m);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index d8a11d3..e6a62c2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics;
import android.content.Context;
+import android.os.UserHandle;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.KeyEvent;
@@ -68,6 +69,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
+ mPasswordField.setTextOperationUser(UserHandle.of(mUserId));
if (mCredentialType == Utils.CREDENTIAL_PIN) {
mPasswordField.setInputType(
InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 8bf2591..496e60d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -286,6 +286,7 @@
if (matched) {
mClearErrorRunnable.run();
+ mLockPatternUtils.userPresent(mEffectiveUserId);
mCallback.onCredentialMatched(attestation);
} else {
if (timeoutMs > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index da5c296..c8e9a68 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -108,7 +108,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
/**
@@ -162,14 +161,6 @@
// Used when ranking updates occur and we check if things should bubble / unbubble
private NotificationListenerService.Ranking mTmpRanking;
- // Saves notification keys of user created "fake" bubbles so that we can allow notifications
- // like these to bubble by default. Doesn't persist across reboots, not a long-term solution.
- private final HashSet<String> mUserCreatedBubbles;
- // If we're auto-bubbling bubbles via a whitelist, we need to track which notifs from that app
- // have been "demoted" back to a notification so that we don't auto-bubbles those again.
- // Doesn't persist across reboots, not a long-term solution.
- private final HashSet<String> mUserBlockedBubbles;
-
// Bubbles get added to the status bar view
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final ZenModeController mZenModeController;
@@ -412,9 +403,6 @@
}
});
- mUserCreatedBubbles = new HashSet<>();
- mUserBlockedBubbles = new HashSet<>();
-
mBubbleIconFactory = new BubbleIconFactory(context);
}
@@ -474,8 +462,7 @@
(entry != null && entry.isRowDismissed() && !isAppCancel)
|| isClearAll || isUserDimiss || isSummaryCancel;
- if (userRemovedNotif || isUserCreatedBubble(key)
- || isSummaryOfUserCreatedBubble(entry)) {
+ if (userRemovedNotif) {
return handleDismissalInterception(entry);
}
@@ -860,27 +847,6 @@
}
/**
- * Whether this bubble was explicitly created by the user via a SysUI affordance.
- */
- boolean isUserCreatedBubble(String key) {
- return mUserCreatedBubbles.contains(key);
- }
-
- boolean isSummaryOfUserCreatedBubble(NotificationEntry entry) {
- if (isSummaryOfBubbles(entry)) {
- List<Bubble> bubbleChildren =
- mBubbleData.getBubblesInGroup(entry.getSbn().getGroupKey());
- for (int i = 0; i < bubbleChildren.size(); i++) {
- // Check if any are user-created (i.e. experimental bubbles)
- if (isUserCreatedBubble(bubbleChildren.get(i).getKey())) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
* Removes the bubble with the given NotificationEntry.
* <p>
* Must be called from the main thread.
@@ -893,37 +859,19 @@
}
private void onEntryAdded(NotificationEntry entry) {
- boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
- boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
- boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
- mContext, entry, previouslyUserCreated, userBlocked);
-
if (mNotificationInterruptStateProvider.shouldBubbleUp(entry)
- && (canLaunchInActivityView(mContext, entry) || wasAdjusted)) {
- if (wasAdjusted && !previouslyUserCreated) {
- // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
- mUserCreatedBubbles.add(entry.getKey());
- }
+ && canLaunchInActivityView(mContext, entry)) {
updateBubble(entry);
}
}
private void onEntryUpdated(NotificationEntry entry) {
- boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
- boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
- boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
- mContext, entry, previouslyUserCreated, userBlocked);
-
boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry)
- && (canLaunchInActivityView(mContext, entry) || wasAdjusted);
+ && canLaunchInActivityView(mContext, entry);
if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
removeBubble(entry, DISMISS_NO_LONGER_BUBBLE);
} else if (shouldBubble) {
- if (wasAdjusted && !previouslyUserCreated) {
- // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
- mUserCreatedBubbles.add(entry.getKey());
- }
updateBubble(entry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 93fb697..3524696 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -193,7 +193,7 @@
+ " mActivityViewStatus=" + mActivityViewStatus
+ " bubble=" + getBubbleKey());
}
- if (mBubble != null && !mBubbleController.isUserCreatedBubble(mBubble.getKey())) {
+ if (mBubble != null) {
// Must post because this is called from a binder thread.
post(() -> mBubbleController.removeBubble(mBubble.getEntry(),
BubbleController.DISMISS_TASK_FINISHED));
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 41dbb48..2060391 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -57,16 +57,13 @@
public class BubbleExperimentConfig {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
- private static final String SHORTCUT_DUMMY_INTENT = "bubble_experiment_shortcut_intent";
- private static PendingIntent sDummyShortcutIntent;
-
private static final int BUBBLE_HEIGHT = 10000;
private static final String ALLOW_ANY_NOTIF_TO_BUBBLE = "allow_any_notif_to_bubble";
private static final boolean ALLOW_ANY_NOTIF_TO_BUBBLE_DEFAULT = false;
private static final String ALLOW_MESSAGE_NOTIFS_TO_BUBBLE = "allow_message_notifs_to_bubble";
- private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = true;
+ private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = false;
private static final String ALLOW_SHORTCUTS_TO_BUBBLE = "allow_shortcuts_to_bubble";
private static final boolean ALLOW_SHORTCUT_TO_BUBBLE_DEFAULT = false;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 1cabe22..0534efc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -374,8 +374,9 @@
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
mExpandedAnimationController.dismissDraggedOutBubble(
- mExpandedAnimationController.getDraggedOutBubble(),
- BubbleStackView.this::dismissMagnetizedObject);
+ mExpandedAnimationController.getDraggedOutBubble() /* bubble */,
+ mDismissTargetContainer.getHeight() /* translationYBy */,
+ BubbleStackView.this::dismissMagnetizedObject /* after */);
hideDismissTarget();
}
};
@@ -405,7 +406,8 @@
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- mStackAnimationController.implodeStack(
+ mStackAnimationController.animateStackDismissal(
+ mDismissTargetContainer.getHeight() /* translationYBy */,
() -> {
resetDesaturationAndDarken();
dismissMagnetizedObject();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 501e502..c96f9a4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -139,22 +139,11 @@
StatusBarNotification sbn = b.getEntry().getSbn();
String packageName = sbn.getPackageName();
- // Real shortcut info for this bubble
String bubbleShortcutId = b.getEntry().getBubbleMetadata().getShortcutId();
if (bubbleShortcutId != null) {
- info.shortcutInfo = BubbleExperimentConfig.getShortcutInfo(c, packageName,
- sbn.getUser(), bubbleShortcutId);
- } else {
- // Check for experimental shortcut
- String shortcutId = sbn.getNotification().getShortcutId();
- if (BubbleExperimentConfig.useShortcutInfoToBubble(c) && shortcutId != null) {
- info.shortcutInfo = BubbleExperimentConfig.getShortcutInfo(c,
- packageName,
- sbn.getUser(), shortcutId);
- }
+ info.shortcutInfo = b.getEntry().getRanking().getShortcutInfo();
}
-
// App name & app icon
PackageManager pm = c.getPackageManager();
ApplicationInfo appInfo;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index d974adc..0002e86 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -329,7 +329,7 @@
}
/** Plays a dismiss animation on the dragged out bubble. */
- public void dismissDraggedOutBubble(View bubble, Runnable after) {
+ public void dismissDraggedOutBubble(View bubble, float translationYBy, Runnable after) {
if (bubble == null) {
return;
}
@@ -337,6 +337,7 @@
.withStiffness(SpringForce.STIFFNESS_HIGH)
.scaleX(1.1f)
.scaleY(1.1f)
+ .translationY(bubble.getTranslationY() + translationYBy)
.alpha(0f, after)
.start();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 00de8b4..5f3a2bd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -647,17 +647,18 @@
}
/**
- * 'Implode' the stack by shrinking the bubbles via chained animations and fading them out.
+ * 'Implode' the stack by shrinking the bubbles, fading them out, and translating them down.
*/
- public void implodeStack(Runnable after) {
- // Pop and fade the bubbles sequentially.
- animationForChildAtIndex(0)
- .scaleX(0.5f)
- .scaleY(0.5f)
- .alpha(0f)
- .withDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
- .withStiffness(SpringForce.STIFFNESS_HIGH)
- .start(after);
+ public void animateStackDismissal(float translationYBy, Runnable after) {
+ animationsForChildrenFromIndex(0, (index, animation) ->
+ animation
+ .scaleX(0.5f)
+ .scaleY(0.5f)
+ .alpha(0f)
+ .translationY(
+ mLayout.getChildAt(index).getTranslationY() + translationYBy)
+ .withStiffness(SpringForce.STIFFNESS_HIGH))
+ .startAll(after);
}
/**
@@ -710,8 +711,6 @@
if (property.equals(DynamicAnimation.TRANSLATION_X)
|| property.equals(DynamicAnimation.TRANSLATION_Y)) {
return index + 1;
- } else if (isStackStuckToTarget()) {
- return index + 1; // Chain all animations in dismiss (scale, alpha, etc. are used).
} else {
return NONE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 62efd8c..8492fef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -82,6 +82,7 @@
protected ComponentName mRecvComponent;
private MediaDevice mDevice;
private boolean mIsRegistered = false;
+ private String mKey;
private final int[] mActionIds;
@@ -203,14 +204,15 @@
* @param bgColor
* @param contentIntent
* @param appNameString
- * @param device
+ * @param key
*/
public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor,
- int bgColor, PendingIntent contentIntent, String appNameString) {
+ int bgColor, PendingIntent contentIntent, String appNameString, String key) {
mToken = token;
mForegroundColor = iconColor;
mBackgroundColor = bgColor;
mController = new MediaController(mContext, mToken);
+ mKey = key;
MediaMetadata mediaMetadata = mController.getMetadata();
@@ -326,6 +328,14 @@
}
/**
+ * Return the original notification's key
+ * @return The notification key
+ */
+ public String getKey() {
+ return mKey;
+ }
+
+ /**
* Check whether this player has an attached media session.
* @return whether there is a controller with a current media session.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index b7658a9..51c157a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -61,6 +61,7 @@
if (!data.enabled) {
seekBarView.setEnabled(false)
seekBarView.getThumb().setAlpha(0)
+ seekBarView.setProgress(0)
elapsedTimeView.setText("")
totalTimeView.setText("")
return
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index dd83e42..1425100 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -77,13 +77,25 @@
val seekAvailable = ((playbackState?.actions ?: 0L) and PlaybackState.ACTION_SEEK_TO) != 0L
val position = playbackState?.position?.toInt()
val duration = mediaMetadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt()
- val enabled = if (duration != null && duration <= 0) false else true
+ val enabled = if (playbackState == null ||
+ playbackState?.getState() == PlaybackState.STATE_NONE ||
+ (duration != null && duration <= 0)) false else true
_data = Progress(enabled, seekAvailable, position, duration, color)
if (shouldPollPlaybackPosition()) {
checkPlaybackPosition()
}
}
+ /**
+ * Puts the seek bar into a resumption state.
+ *
+ * This should be called when the media session behind the controller has been destroyed.
+ */
+ @AnyThread
+ fun clearController() = bgExecutor.execute {
+ _data = _data.copy(enabled = false)
+ }
+
@AnyThread
private fun checkPlaybackPosition(): Runnable = bgExecutor.executeDelayed({
val currentPosition = controller?.playbackState?.position?.toInt()
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 c3779ef..ba9a30f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -339,18 +339,18 @@
@Override
public void onPipTransitionFinished(ComponentName activity, int direction) {
- onPipTransitionFinishedOrCanceled();
+ onPipTransitionFinishedOrCanceled(direction);
}
@Override
public void onPipTransitionCanceled(ComponentName activity, int direction) {
- onPipTransitionFinishedOrCanceled();
+ onPipTransitionFinishedOrCanceled(direction);
}
- private void onPipTransitionFinishedOrCanceled() {
+ private void onPipTransitionFinishedOrCanceled(int direction) {
// Re-enable touches after the animation completes
mTouchHandler.setTouchEnabled(true);
- mTouchHandler.onPinnedStackAnimationEnded();
+ mTouchHandler.onPinnedStackAnimationEnded(direction);
mMenuController.onPinnedStackAnimationEnded();
}
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 2b9b171..ec15dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -54,6 +54,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
@@ -129,9 +130,7 @@
}
};
- private Handler mHandler = new Handler();
- private Messenger mToControllerMessenger;
- private Messenger mMessenger = new Messenger(new Handler() {
+ private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -174,7 +173,9 @@
}
}
}
- });
+ };
+ private Messenger mToControllerMessenger;
+ private Messenger mMessenger = new Messenger(mHandler);
private final Runnable mFinishRunnable = new Runnable() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index d660b67..61ed40d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -30,6 +30,7 @@
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
@@ -122,7 +123,7 @@
private boolean mStartActivityRequested;
private long mStartActivityRequestedTime;
private Messenger mToActivityMessenger;
- private Handler mHandler = new Handler() {
+ private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -133,15 +134,15 @@
break;
}
case MESSAGE_EXPAND_PIP: {
- mListeners.forEach(l -> l.onPipExpand());
+ mListeners.forEach(Listener::onPipExpand);
break;
}
case MESSAGE_DISMISS_PIP: {
- mListeners.forEach(l -> l.onPipDismiss());
+ mListeners.forEach(Listener::onPipDismiss);
break;
}
case MESSAGE_SHOW_MENU: {
- mListeners.forEach(l -> l.onPipShowMenu());
+ mListeners.forEach(Listener::onPipShowMenu);
break;
}
case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
@@ -259,6 +260,8 @@
if (DEBUG) {
Log.d(TAG, "showMenu() state=" + menuState
+ " hasActivity=" + (mToActivityMessenger != null)
+ + " allowMenuTimeout=" + allowMenuTimeout
+ + " willResizeMenu=" + willResizeMenu
+ " callers=\n" + Debug.getCallers(5, " "));
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 0b07655..d80f18a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -56,7 +56,6 @@
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
private final PipBoundsHandler mPipBoundsHandler;
- private final PipTouchHandler mPipTouchHandler;
private final PipMotionHelper mMotionHelper;
private final int mDisplayId;
private final Executor mMainExecutor;
@@ -70,10 +69,10 @@
private final Rect mTmpBounds = new Rect();
private final int mDelta;
- private boolean mAllowGesture = false;
+ private boolean mAllowGesture;
private boolean mIsAttached;
private boolean mIsEnabled;
- private boolean mEnablePipResize;
+ private boolean mEnableUserResize;
private InputMonitor mInputMonitor;
private InputEventReceiver mInputEventReceiver;
@@ -82,21 +81,20 @@
private int mCtrlType;
public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
- PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper,
- DeviceConfigProxy deviceConfig, PipTaskOrganizer pipTaskOrganizer) {
+ PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig,
+ PipTaskOrganizer pipTaskOrganizer) {
final Resources res = context.getResources();
context.getDisplay().getMetrics(mDisplayMetrics);
mDisplayId = context.getDisplayId();
mMainExecutor = context.getMainExecutor();
mPipBoundsHandler = pipBoundsHandler;
- mPipTouchHandler = pipTouchHandler;
mMotionHelper = motionHelper;
mPipTaskOrganizer = pipTaskOrganizer;
context.getDisplay().getRealSize(mMaxSize);
mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
- mEnablePipResize = DeviceConfig.getBoolean(
+ mEnableUserResize = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_USER_RESIZE,
/* defaultValue = */ true);
@@ -105,7 +103,7 @@
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
if (properties.getKeyset().contains(PIP_USER_RESIZE)) {
- mEnablePipResize = properties.getBoolean(
+ mEnableUserResize = properties.getBoolean(
PIP_USER_RESIZE, /* defaultValue = */ true);
}
}
@@ -134,7 +132,7 @@
}
private void updateIsEnabled() {
- boolean isEnabled = mIsAttached && mEnablePipResize;
+ boolean isEnabled = mIsAttached && mEnableUserResize;
if (isEnabled == mIsEnabled) {
return;
}
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 79d2edd..f5c83c1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -16,6 +16,7 @@
package com.android.systemui.pip.phone;
+import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
@@ -56,6 +57,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.systemui.R;
+import com.android.systemui.pip.PipAnimationController;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.pip.PipTaskOrganizer;
@@ -229,7 +231,7 @@
mMotionHelper = new PipMotionHelper(mContext, activityTaskManager, pipTaskOrganizer,
mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator);
mPipResizeGestureHandler =
- new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper,
+ new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
deviceConfig, pipTaskOrganizer);
mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
() -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
@@ -343,11 +345,16 @@
mPipResizeGestureHandler.onActivityUnpinned();
}
- public void onPinnedStackAnimationEnded() {
+ public void onPinnedStackAnimationEnded(
+ @PipAnimationController.TransitionDirection int direction) {
// Always synchronize the motion helper bounds once PiP animations finish
mMotionHelper.synchronizePinnedStackBounds();
updateMovementBounds();
- mResizedBounds.set(mMotionHelper.getBounds());
+ if (direction == TRANSITION_DIRECTION_TO_PIP) {
+ // updates mResizedBounds only if it's an entering PiP animation
+ // mResized should be otherwise updated in setMenuState.
+ mResizedBounds.set(mMotionHelper.getBounds());
+ }
if (mShowPipMenuOnAnimationEnd) {
mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index e636707..e4bcb09 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -99,15 +99,14 @@
* @param bgColor background color
* @param actionsContainer a LinearLayout containing the media action buttons
* @param notif reference to original notification
- * @param device current playback device
+ * @param key original notification's key
*/
public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor,
- int bgColor, View actionsContainer, Notification notif) {
+ int bgColor, View actionsContainer, Notification notif, String key) {
String appName = Notification.Builder.recoverBuilder(getContext(), notif)
.loadHeaderAppName();
- super.setMediaSession(token, icon, iconColor, bgColor, notif.contentIntent,
- appName);
+ super.setMediaSession(token, icon, iconColor, bgColor, notif.contentIntent, appName, key);
// Media controls
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
@@ -171,6 +170,8 @@
public void clearControls() {
super.clearControls();
+ mSeekBarViewModel.clearController();
+
View guts = mMediaNotifView.findViewById(R.id.media_guts);
View options = mMediaNotifView.findViewById(R.id.qs_media_controls_options);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 0566b2e..40c8aad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -208,9 +208,10 @@
* @param bgColor
* @param actionsContainer
* @param notif
+ * @param key
*/
public void addMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
- View actionsContainer, StatusBarNotification notif) {
+ View actionsContainer, StatusBarNotification notif, String key) {
if (!useQsMediaPlayer(mContext)) {
// Shouldn't happen, but just in case
Log.e(TAG, "Tried to add media session without player!");
@@ -225,13 +226,12 @@
String packageName = notif.getPackageName();
for (QSMediaPlayer p : mMediaPlayers) {
if (p.getMediaSessionToken().equals(token)) {
- Log.d(TAG, "a player for this session already exists");
+ Log.d(TAG, "Found matching player by token " + packageName);
player = p;
break;
- }
-
- if (packageName.equals(p.getMediaPlayerPackage())) {
- Log.d(TAG, "found an old session for this app");
+ } else if (packageName.equals(p.getMediaPlayerPackage()) && key.equals(p.getKey())) {
+ // Also match if it's the same package and notification key
+ Log.d(TAG, "Found matching player by package " + packageName + ", " + key);
player = p;
break;
}
@@ -267,7 +267,7 @@
Log.d(TAG, "setting player session");
player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer,
- notif.getNotification());
+ notif.getNotification(), key);
if (mMediaPlayers.size() > 0) {
((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index 0ba4cb1..7946779 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -67,9 +67,10 @@
* @param actionsToShow indices of which actions to display in the mini player
* (max 3: Notification.MediaStyle.MAX_MEDIA_BUTTONS_IN_COMPACT)
* @param contentIntent Intent to send when user taps on the view
+ * @param key original notification's key
*/
public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
- View actionsContainer, int[] actionsToShow, PendingIntent contentIntent) {
+ View actionsContainer, int[] actionsToShow, PendingIntent contentIntent, String key) {
// Only update if this is a different session and currently playing
String oldPackage = "";
if (getController() != null) {
@@ -84,7 +85,7 @@
return;
}
- super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, null);
+ super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, null, key);
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
int i = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index a271993..ab2cffa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row;
import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
@@ -33,6 +34,7 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -51,6 +53,7 @@
import android.transition.TransitionSet;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Slog;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
@@ -91,6 +94,7 @@
private String mConversationId;
private StatusBarNotification mSbn;
private boolean mIsDeviceProvisioned;
+ private int mAppBubble;
private TextView mPriorityDescriptionView;
private TextView mDefaultDescriptionView;
@@ -206,6 +210,13 @@
mNotificationChannel = NotificationChannelHelper.createConversationChannelIfNeeded(
getContext(), mINotificationManager, entry, mNotificationChannel);
+ try {
+ mAppBubble = mINotificationManager.getBubblePreferenceForPackage(mPackageName, mAppUid);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't reach OS", e);
+ mAppBubble = BUBBLE_PREFERENCE_SELECTED;
+ }
+
bindHeader();
bindActions();
@@ -227,6 +238,11 @@
snooze.setOnClickListener(mOnSnoozeClick);
*/
+ if (mAppBubble == BUBBLE_PREFERENCE_ALL) {
+ ((TextView) findViewById(R.id.default_summary)).setText(getResources().getString(
+ R.string.notification_channel_summary_default_with_bubbles, mAppName));
+ }
+
findViewById(R.id.priority).setOnClickListener(mOnFavoriteClick);
findViewById(R.id.default_behavior).setOnClickListener(mOnDefaultClick);
findViewById(R.id.silence).setOnClickListener(mOnMuteClick);
@@ -264,7 +280,6 @@
// bindName();
bindPackage();
bindIcon(mNotificationChannel.isImportantConversation());
-
}
private void bindIcon(boolean important) {
@@ -560,10 +575,7 @@
!mChannelToUpdate.isImportantConversation());
if (mChannelToUpdate.isImportantConversation()) {
mChannelToUpdate.setAllowBubbles(true);
- int currentPref =
- mINotificationManager.getBubblePreferenceForPackage(
- mAppPkg, mAppUid);
- if (currentPref == BUBBLE_PREFERENCE_NONE) {
+ if (mAppBubble == BUBBLE_PREFERENCE_NONE) {
mINotificationManager.setBubblesAllowed(mAppPkg, mAppUid,
BUBBLE_PREFERENCE_SELECTED);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 7ac0662..e20be2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -18,7 +18,6 @@
import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
-import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.app.Notification;
import android.content.Context;
@@ -133,15 +132,6 @@
if (mAppOps != null) {
mAppOps.setOnClickListener(listener);
}
- if (mCameraIcon != null) {
- mCameraIcon.setOnClickListener(listener);
- }
- if (mMicIcon != null) {
- mMicIcon.setOnClickListener(listener);
- }
- if (mOverlayIcon != null) {
- mOverlayIcon.setOnClickListener(listener);
- }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 874d81d..2da2724 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -193,7 +193,8 @@
mBackgroundColor,
mActions,
compactActions,
- notif.contentIntent);
+ notif.contentIntent,
+ sbn.getKey());
QSPanel bigPanel = ctrl.getNotificationShadeView().findViewById(
com.android.systemui.R.id.quick_settings_panel);
bigPanel.addMediaSession(token,
@@ -201,7 +202,8 @@
tintColor,
mBackgroundColor,
mActions,
- sbn);
+ sbn,
+ sbn.getKey());
}
boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index f316d04..28a3d6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -86,7 +86,29 @@
}
@Test
- fun updateDuration() {
+ fun updateDurationWithPlayback() {
+ // GIVEN that the duration is contained within the metadata
+ val duration = 12000L
+ val metadata = MediaMetadata.Builder().run {
+ putLong(MediaMetadata.METADATA_KEY_DURATION, duration)
+ build()
+ }
+ whenever(mockController.getMetadata()).thenReturn(metadata)
+ // AND a valid playback state (ie. media session is not destroyed)
+ val state = PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
+ // WHEN the controller is updated
+ viewModel.updateController(mockController, Color.RED)
+ // THEN the duration is extracted
+ assertThat(viewModel.progress.value!!.duration).isEqualTo(duration)
+ assertThat(viewModel.progress.value!!.enabled).isTrue()
+ }
+
+ @Test
+ fun updateDurationWithoutPlayback() {
// GIVEN that the duration is contained within the metadata
val duration = 12000L
val metadata = MediaMetadata.Builder().run {
@@ -98,7 +120,7 @@
viewModel.updateController(mockController, Color.RED)
// THEN the duration is extracted
assertThat(viewModel.progress.value!!.duration).isEqualTo(duration)
- assertThat(viewModel.progress.value!!.enabled).isTrue()
+ assertThat(viewModel.progress.value!!.enabled).isFalse()
}
@Test
@@ -110,6 +132,12 @@
build()
}
whenever(mockController.getMetadata()).thenReturn(metadata)
+ // AND a valid playback state (ie. media session is not destroyed)
+ val state = PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
viewModel.updateController(mockController, Color.RED)
// THEN the seek bar is disabled
@@ -125,6 +153,12 @@
build()
}
whenever(mockController.getMetadata()).thenReturn(metadata)
+ // AND a valid playback state (ie. media session is not destroyed)
+ val state = PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
viewModel.updateController(mockController, Color.RED)
// THEN the seek bar is disabled
@@ -372,4 +406,30 @@
// THEN an update task is queued
assertThat(fakeExecutor.numPending()).isEqualTo(1)
}
+
+ @Test
+ fun clearSeekBar() {
+ // GIVEN that the duration is contained within the metadata
+ val metadata = MediaMetadata.Builder().run {
+ putLong(MediaMetadata.METADATA_KEY_DURATION, 12000L)
+ build()
+ }
+ whenever(mockController.getMetadata()).thenReturn(metadata)
+ // AND a valid playback state (ie. media session is not destroyed)
+ val state = PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
+ // AND the controller has been updated
+ viewModel.updateController(mockController, Color.RED)
+ // WHEN the controller is cleared on the event when the session is destroyed
+ viewModel.clearController()
+ with(fakeExecutor) {
+ advanceClockToNext()
+ runAllReady()
+ }
+ // THEN the seek bar is disabled
+ assertThat(viewModel.progress.value!!.enabled).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index b6bd5e2..6bcaee1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.notification.row;
import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
@@ -458,7 +460,9 @@
}
@Test
- public void testBindNotification_defaultSelected_notFave_notSilent() {
+ public void testBindNotification_defaultSelected_notFave_notSilent() throws Exception {
+ when(mMockINotificationManager.getBubblePreferenceForPackage(anyString(), anyInt()))
+ .thenReturn(BUBBLE_PREFERENCE_SELECTED);
mConversationChannel.setImportance(IMPORTANCE_HIGH);
mConversationChannel.setImportantConversation(false);
mConversationChannel.setAllowBubbles(true);
@@ -476,6 +480,35 @@
true);
View view = mNotificationInfo.findViewById(R.id.default_behavior);
assertThat(view.isSelected()).isTrue();
+ assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
+ mContext.getString(R.string.notification_channel_summary_default));
+ }
+
+ @Test
+ public void testBindNotification_default_allCanBubble() throws Exception {
+ when(mMockINotificationManager.getBubblePreferenceForPackage(anyString(), anyInt()))
+ .thenReturn(BUBBLE_PREFERENCE_ALL);
+ when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
+ mConversationChannel.setImportance(IMPORTANCE_HIGH);
+ mConversationChannel.setImportantConversation(false);
+ mConversationChannel.setAllowBubbles(true);
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mEntry,
+ null,
+ null,
+ mIconFactory,
+ true);
+ View view = mNotificationInfo.findViewById(R.id.default_behavior);
+ assertThat(view.isSelected()).isTrue();
+ assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
+ mContext.getString(R.string.notification_channel_summary_default_with_bubbles,
+ "App Name"));
}
@Test
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
index 1dc8227..2b2fe45 100644
--- a/packages/Tethering/AndroidManifest.xml
+++ b/packages/Tethering/AndroidManifest.xml
@@ -34,11 +34,14 @@
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <protected-broadcast android:name="com.android.server.connectivity.tethering.DISABLE_TETHERING" />
+
<application
android:process="com.android.networkstack.process"
android:extractNativeLibs="false"
diff --git a/packages/Tethering/res/values-mcc204-mnc04/strings.xml b/packages/Tethering/res/values-mcc204-mnc04/strings.xml
deleted file mode 100644
index 9dadd49..0000000
--- a/packages/Tethering/res/values-mcc204-mnc04/strings.xml
+++ /dev/null
@@ -1,30 +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.
--->
-<resources>
- <!-- String for no upstream notification title [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_title">Tethering has no internet</string>
- <!-- String for no upstream notification title [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_message">Devices can\u2019t connect</string>
- <!-- String for no upstream notification disable button [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_disable_button">Turn off tethering</string>
-
- <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
- <string name="upstream_roaming_notification_title">Hotspot or tethering is on</string>
- <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
- <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string>
- <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] -->
- <string name="upstream_roaming_notification_continue_button">Continue</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004/config.xml b/packages/Tethering/res/values-mcc310-mnc004/config.xml
new file mode 100644
index 0000000..8c627d5
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004/config.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<resources>
+ <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
+ "0" for disable this feature. -->
+ <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/Tethering/res/values-mcc311-mnc480/config.xml b/packages/Tethering/res/values-mcc311-mnc480/config.xml
new file mode 100644
index 0000000..8c627d5
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480/config.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<resources>
+ <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
+ "0" for disable this feature. -->
+ <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 430fdc4..52aa5bb 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -202,4 +202,10 @@
<string name="tethering_notification_title">@string/tethered_notification_title</string>
<!-- String for tether enable notification message. -->
<string name="tethering_notification_message">@string/tethered_notification_message</string>
+
+ <!-- No upstream notification is shown when there is a downstream but no upstream that is able
+ to do the tethering. -->
+ <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
+ "-1" for disable this feature. -->
+ <integer name="delay_to_show_no_upstream_after_no_backhaul">-1</integer>
</resources>
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 4c7b2d4..049a9f6 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -38,8 +38,6 @@
import android.net.util.SharedLog;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
@@ -75,11 +73,6 @@
private final ComponentName mSilentProvisioningService;
private static final int MS_PER_HOUR = 60 * 60 * 1000;
- private static final int EVENT_START_PROVISIONING = 0;
- private static final int EVENT_STOP_PROVISIONING = 1;
- private static final int EVENT_UPSTREAM_CHANGED = 2;
- private static final int EVENT_MAYBE_RUN_PROVISIONING = 3;
- private static final int EVENT_GET_ENTITLEMENT_VALUE = 4;
// The ArraySet contains enabled downstream types, ex:
// {@link TetheringManager.TETHERING_WIFI}
@@ -90,7 +83,7 @@
private final int mPermissionChangeMessageCode;
private final SharedLog mLog;
private final SparseIntArray mEntitlementCacheValue;
- private final EntitlementHandler mHandler;
+ private final Handler mHandler;
private final StateMachine mTetherMasterSM;
// Key: TetheringManager.TETHERING_*(downstream).
// Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result).
@@ -112,10 +105,7 @@
mEntitlementCacheValue = new SparseIntArray();
mTetherMasterSM = tetherMasterSM;
mPermissionChangeMessageCode = permissionChangeMessageCode;
- final Handler masterHandler = tetherMasterSM.getHandler();
- // Create entitlement's own handler which is associated with TetherMaster thread
- // let all entitlement processes run in the same thread.
- mHandler = new EntitlementHandler(masterHandler.getLooper());
+ mHandler = tetherMasterSM.getHandler();
mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
null, mHandler);
mSilentProvisioningService = ComponentName.unflattenFromString(
@@ -172,14 +162,9 @@
* provisioning app UI if there is one.
*/
public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) {
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_START_PROVISIONING,
- downstreamType, encodeBool(showProvisioningUi)));
- }
+ if (!isValidDownstreamType(downstreamType)) return;
- private void handleStartProvisioningIfNeeded(int type, boolean showProvisioningUi) {
- if (!isValidDownstreamType(type)) return;
-
- if (!mCurrentTethers.contains(type)) mCurrentTethers.add(type);
+ if (!mCurrentTethers.contains(downstreamType)) mCurrentTethers.add(downstreamType);
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
if (isTetherProvisioningRequired(config)) {
@@ -192,9 +177,9 @@
// till upstream change to cellular.
if (mUsingCellularAsUpstream) {
if (showProvisioningUi) {
- runUiTetherProvisioning(type, config.activeDataSubId);
+ runUiTetherProvisioning(downstreamType, config.activeDataSubId);
} else {
- runSilentTetherProvisioning(type, config.activeDataSubId);
+ runSilentTetherProvisioning(downstreamType, config.activeDataSubId);
}
mNeedReRunProvisioningUi = false;
} else {
@@ -211,10 +196,6 @@
* @param type tethering type from TetheringManager.TETHERING_{@code *}
*/
public void stopProvisioningIfNeeded(int type) {
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_STOP_PROVISIONING, type, 0));
- }
-
- private void handleStopProvisioningIfNeeded(int type) {
if (!isValidDownstreamType(type)) return;
mCurrentTethers.remove(type);
@@ -230,11 +211,6 @@
* @param isCellular whether tethering upstream is cellular.
*/
public void notifyUpstream(boolean isCellular) {
- mHandler.sendMessage(mHandler.obtainMessage(
- EVENT_UPSTREAM_CHANGED, encodeBool(isCellular), 0));
- }
-
- private void handleNotifyUpstream(boolean isCellular) {
if (DBG) {
mLog.i("notifyUpstream: " + isCellular
+ ", mCellularUpstreamPermitted: " + mCellularUpstreamPermitted
@@ -244,16 +220,17 @@
if (mUsingCellularAsUpstream) {
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- handleMaybeRunProvisioning(config);
+ maybeRunProvisioning(config);
}
}
/** Run provisioning if needed */
public void maybeRunProvisioning() {
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_MAYBE_RUN_PROVISIONING));
+ final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
+ maybeRunProvisioning(config);
}
- private void handleMaybeRunProvisioning(final TetheringConfiguration config) {
+ private void maybeRunProvisioning(final TetheringConfiguration config) {
if (mCurrentTethers.size() == 0 || !isTetherProvisioningRequired(config)) {
return;
}
@@ -319,7 +296,7 @@
}
if (mUsingCellularAsUpstream) {
- handleMaybeRunProvisioning(config);
+ maybeRunProvisioning(config);
}
}
@@ -494,46 +471,6 @@
}
};
- private class EntitlementHandler extends Handler {
- EntitlementHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_START_PROVISIONING:
- handleStartProvisioningIfNeeded(msg.arg1, toBool(msg.arg2));
- break;
- case EVENT_STOP_PROVISIONING:
- handleStopProvisioningIfNeeded(msg.arg1);
- break;
- case EVENT_UPSTREAM_CHANGED:
- handleNotifyUpstream(toBool(msg.arg1));
- break;
- case EVENT_MAYBE_RUN_PROVISIONING:
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- handleMaybeRunProvisioning(config);
- break;
- case EVENT_GET_ENTITLEMENT_VALUE:
- handleRequestLatestTetheringEntitlementValue(msg.arg1,
- (ResultReceiver) msg.obj, toBool(msg.arg2));
- break;
- default:
- mLog.log("Unknown event: " + msg.what);
- break;
- }
- }
- }
-
- private static boolean toBool(int encodedBoolean) {
- return encodedBoolean != 0;
- }
-
- private static int encodeBool(boolean b) {
- return b ? 1 : 0;
- }
-
private static boolean isValidDownstreamType(int type) {
switch (type) {
case TETHERING_BLUETOOTH:
@@ -644,13 +581,6 @@
/** Get the last value of the tethering entitlement check. */
public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
boolean showEntitlementUi) {
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_GET_ENTITLEMENT_VALUE,
- downstream, encodeBool(showEntitlementUi), receiver));
-
- }
-
- private void handleRequestLatestTetheringEntitlementValue(int downstream,
- ResultReceiver receiver, boolean showEntitlementUi) {
if (!isValidDownstreamType(downstream)) {
receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null);
return;
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index f3cead9..da8bf54 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -257,7 +257,7 @@
mContext = mDeps.getContext();
mNetd = mDeps.getINetd(mContext);
mLooper = mDeps.getTetheringLooper();
- mNotificationUpdater = mDeps.getNotificationUpdater(mContext);
+ mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper);
mPublicSync = new Object();
@@ -337,6 +337,11 @@
filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED);
mContext.registerReceiver(mStateReceiver, filter, null, mHandler);
+ final IntentFilter noUpstreamFilter = new IntentFilter();
+ noUpstreamFilter.addAction(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING);
+ mContext.registerReceiver(
+ mStateReceiver, noUpstreamFilter, PERMISSION_MAINLINE_NETWORK_STACK, mHandler);
+
final WifiManager wifiManager = getWifiManager();
if (wifiManager != null) {
wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
@@ -855,6 +860,8 @@
} else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) {
mLog.log("OBSERVED data saver changed");
handleDataSaverChanged();
+ } else if (action.equals(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING)) {
+ untetherAll();
}
}
@@ -922,8 +929,10 @@
case WifiManager.WIFI_AP_STATE_ENABLED:
enableWifiIpServingLocked(ifname, ipmode);
break;
- case WifiManager.WIFI_AP_STATE_DISABLED:
case WifiManager.WIFI_AP_STATE_DISABLING:
+ // We can see this state on the way to disabled.
+ break;
+ case WifiManager.WIFI_AP_STATE_DISABLED:
case WifiManager.WIFI_AP_STATE_FAILED:
default:
disableWifiIpServingLocked(ifname, curState);
@@ -1944,10 +1953,12 @@
/** Get the latest value of the tethering entitlement check. */
void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
boolean showEntitlementUi) {
- if (receiver != null) {
+ if (receiver == null) return;
+
+ mHandler.post(() -> {
mEntitlementMgr.requestLatestTetheringEntitlementResult(type, receiver,
showEntitlementUi);
- }
+ });
}
/** Register tethering event callback */
@@ -2011,6 +2022,7 @@
} finally {
mTetheringEventCallbacks.finishBroadcast();
}
+ mNotificationUpdater.onUpstreamNetworkChanged(network);
}
private void reportConfigurationChanged(TetheringConfigurationParcel config) {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index 893c582..9b54b5f 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -106,8 +106,9 @@
/**
* Get a reference to the TetheringNotificationUpdater to be used by tethering.
*/
- public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx) {
- return new TetheringNotificationUpdater(ctx);
+ public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx,
+ @NonNull final Looper looper) {
+ return new TetheringNotificationUpdater(ctx, looper);
}
/**
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
index 4287056..ff83fd1 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
@@ -19,18 +19,25 @@
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.text.TextUtils.isEmpty;
import android.app.Notification;
+import android.app.Notification.Action;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
import android.content.res.Resources;
+import android.net.Network;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
-import android.text.TextUtils;
+import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.SparseArray;
@@ -39,9 +46,13 @@
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.Arrays;
+import java.util.List;
+
/**
* A class to display tethering-related notifications.
*
@@ -58,12 +69,22 @@
private static final String WIFI_DOWNSTREAM = "WIFI";
private static final String USB_DOWNSTREAM = "USB";
private static final String BLUETOOTH_DOWNSTREAM = "BT";
+ @VisibleForTesting
+ static final String ACTION_DISABLE_TETHERING =
+ "com.android.server.connectivity.tethering.DISABLE_TETHERING";
private static final boolean NOTIFY_DONE = true;
private static final boolean NO_NOTIFY = false;
- // Id to update and cancel tethering notification. Must be unique within the tethering app.
- private static final int ENABLE_NOTIFICATION_ID = 1000;
+ @VisibleForTesting
+ static final int EVENT_SHOW_NO_UPSTREAM = 1;
+ // Id to update and cancel enable notification. Must be unique within the tethering app.
+ @VisibleForTesting
+ static final int ENABLE_NOTIFICATION_ID = 1000;
// Id to update and cancel restricted notification. Must be unique within the tethering app.
- private static final int RESTRICTED_NOTIFICATION_ID = 1001;
+ @VisibleForTesting
+ static final int RESTRICTED_NOTIFICATION_ID = 1001;
+ // Id to update and cancel no upstream notification. Must be unique within the tethering app.
+ @VisibleForTesting
+ static final int NO_UPSTREAM_NOTIFICATION_ID = 1002;
@VisibleForTesting
static final int NO_ICON_ID = 0;
@VisibleForTesting
@@ -71,14 +92,16 @@
private final Context mContext;
private final NotificationManager mNotificationManager;
private final NotificationChannel mChannel;
+ private final Handler mHandler;
// WARNING : the constructor is called on a different thread. Thread safety therefore
- // relies on this value being initialized to 0, and not any other value. If you need
+ // relies on these values being initialized to 0 or false, and not any other value. If you need
// to change this, you will need to change the thread where the constructor is invoked,
// or to introduce synchronization.
// Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
// This value has to be made 1 2 and 4, and OR'd with the others.
private int mDownstreamTypesMask = DOWNSTREAM_NONE;
+ private boolean mNoUpstream = false;
// WARNING : this value is not able to being initialized to 0 and must have volatile because
// telephony service is not guaranteed that is up before tethering service starts. If telephony
@@ -87,10 +110,30 @@
// INVALID_SUBSCRIPTION_ID.
private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID})
+ @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID})
@interface NotificationId {}
- public TetheringNotificationUpdater(@NonNull final Context context) {
+ private static final class MccMncOverrideInfo {
+ public final List<String> visitedMccMncs;
+ public final int homeMcc;
+ public final int homeMnc;
+ MccMncOverrideInfo(List<String> visitedMccMncs, int mcc, int mnc) {
+ this.visitedMccMncs = visitedMccMncs;
+ this.homeMcc = mcc;
+ this.homeMnc = mnc;
+ }
+ }
+
+ private static final SparseArray<MccMncOverrideInfo> sCarrierIdToMccMnc = new SparseArray<>();
+
+ static {
+ // VZW
+ sCarrierIdToMccMnc.put(
+ 1839, new MccMncOverrideInfo(Arrays.asList(new String[] {"20404"}), 311, 480));
+ }
+
+ public TetheringNotificationUpdater(@NonNull final Context context,
+ @NonNull final Looper looper) {
mContext = context;
mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0)
.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -99,6 +142,22 @@
context.getResources().getString(R.string.notification_channel_tethering_status),
NotificationManager.IMPORTANCE_LOW);
mNotificationManager.createNotificationChannel(mChannel);
+ mHandler = new NotificationHandler(looper);
+ }
+
+ private class NotificationHandler extends Handler {
+ NotificationHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case EVENT_SHOW_NO_UPSTREAM:
+ notifyTetheringNoUpstream();
+ break;
+ }
+ }
}
/** Called when downstream has changed */
@@ -106,6 +165,7 @@
if (mDownstreamTypesMask == downstreamTypesMask) return;
mDownstreamTypesMask = downstreamTypesMask;
updateEnableNotification();
+ updateNoUpstreamNotification();
}
/** Called when active data subscription id changed */
@@ -113,21 +173,62 @@
if (mActiveDataSubId == subId) return;
mActiveDataSubId = subId;
updateEnableNotification();
+ updateNoUpstreamNotification();
}
+ /** Called when upstream network changed */
+ public void onUpstreamNetworkChanged(@Nullable final Network network) {
+ final boolean isNoUpstream = (network == null);
+ if (mNoUpstream == isNoUpstream) return;
+ mNoUpstream = isNoUpstream;
+ updateNoUpstreamNotification();
+ }
+
+ @NonNull
@VisibleForTesting
- Resources getResourcesForSubId(@NonNull final Context c, final int subId) {
- return SubscriptionManager.getResourcesForSubId(c, subId);
+ final Handler getHandler() {
+ return mHandler;
+ }
+
+ @NonNull
+ @VisibleForTesting
+ Resources getResourcesForSubId(@NonNull final Context context, final int subId) {
+ final Resources res = SubscriptionManager.getResourcesForSubId(context, subId);
+ final TelephonyManager tm =
+ ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
+ .createForSubscriptionId(mActiveDataSubId);
+ final int carrierId = tm.getSimCarrierId();
+ final String mccmnc = tm.getSimOperator();
+ final MccMncOverrideInfo overrideInfo = sCarrierIdToMccMnc.get(carrierId);
+ if (overrideInfo != null && overrideInfo.visitedMccMncs.contains(mccmnc)) {
+ // Re-configure MCC/MNC value to specific carrier to get right resources.
+ final Configuration config = res.getConfiguration();
+ config.mcc = overrideInfo.homeMcc;
+ config.mnc = overrideInfo.homeMnc;
+ return context.createConfigurationContext(config).getResources();
+ }
+ return res;
}
private void updateEnableNotification() {
- final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE;
+ final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
if (tetheringInactive || setupNotification() == NO_NOTIFY) {
clearNotification(ENABLE_NOTIFICATION_ID);
}
}
+ private void updateNoUpstreamNotification() {
+ final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
+
+ if (tetheringInactive
+ || !mNoUpstream
+ || setupNoUpstreamNotification() == NO_NOTIFY) {
+ clearNotification(NO_UPSTREAM_NOTIFICATION_ID);
+ mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM);
+ }
+ }
+
@VisibleForTesting
void tetheringRestrictionLifted() {
clearNotification(RESTRICTED_NOTIFICATION_ID);
@@ -142,9 +243,38 @@
final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
final String title = res.getString(R.string.disable_tether_notification_title);
final String message = res.getString(R.string.disable_tether_notification_message);
+ if (isEmpty(title) || isEmpty(message)) return;
+
+ final PendingIntent pi = PendingIntent.getActivity(
+ mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+ 0 /* requestCode */,
+ new Intent(Settings.ACTION_TETHER_SETTINGS),
+ Intent.FLAG_ACTIVITY_NEW_TASK,
+ null /* options */);
showNotification(R.drawable.stat_sys_tether_general, title, message,
- RESTRICTED_NOTIFICATION_ID);
+ RESTRICTED_NOTIFICATION_ID, pi, new Action[0]);
+ }
+
+ private void notifyTetheringNoUpstream() {
+ final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+ final String title = res.getString(R.string.no_upstream_notification_title);
+ final String message = res.getString(R.string.no_upstream_notification_message);
+ final String disableButton =
+ res.getString(R.string.no_upstream_notification_disable_button);
+ if (isEmpty(title) || isEmpty(message) || isEmpty(disableButton)) return;
+
+ final Intent intent = new Intent(ACTION_DISABLE_TETHERING);
+ intent.setPackage(mContext.getPackageName());
+ final PendingIntent pi = PendingIntent.getBroadcast(
+ mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+ 0 /* requestCode */,
+ intent,
+ 0 /* flags */);
+ final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build();
+
+ showNotification(R.drawable.stat_sys_tether_general, title, message,
+ NO_UPSTREAM_NOTIFICATION_ID, null /* pendingIntent */, action);
}
/**
@@ -179,12 +309,13 @@
*
* @return {@link android.util.SparseArray} with downstream types and icon id info.
*/
+ @NonNull
@VisibleForTesting
SparseArray<Integer> getIcons(@ArrayRes int id, @NonNull Resources res) {
final String[] array = res.getStringArray(id);
final SparseArray<Integer> icons = new SparseArray<>();
for (String config : array) {
- if (TextUtils.isEmpty(config)) continue;
+ if (isEmpty(config)) continue;
final String[] elements = config.split(";");
if (elements.length != 2) {
@@ -204,6 +335,18 @@
return icons;
}
+ private boolean setupNoUpstreamNotification() {
+ final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+ final int delayToShowUpstreamNotification =
+ res.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul);
+
+ if (delayToShowUpstreamNotification < 0) return NO_NOTIFY;
+
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_SHOW_NO_UPSTREAM),
+ delayToShowUpstreamNotification);
+ return NOTIFY_DONE;
+ }
+
private boolean setupNotification() {
final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
final SparseArray<Integer> downstreamIcons =
@@ -214,17 +357,22 @@
final String title = res.getString(R.string.tethering_notification_title);
final String message = res.getString(R.string.tethering_notification_message);
+ if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY;
- showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID);
+ final PendingIntent pi = PendingIntent.getActivity(
+ mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+ 0 /* requestCode */,
+ new Intent(Settings.ACTION_TETHER_SETTINGS),
+ Intent.FLAG_ACTIVITY_NEW_TASK,
+ null /* options */);
+
+ showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID, pi, new Action[0]);
return NOTIFY_DONE;
}
private void showNotification(@DrawableRes final int iconId, @NonNull final String title,
- @NonNull final String message, @NotificationId final int id) {
- final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS);
- final PendingIntent pi = PendingIntent.getActivity(
- mContext.createContextAsUser(UserHandle.CURRENT, 0),
- 0 /* requestCode */, intent, 0 /* flags */, null /* options */);
+ @NonNull final String message, @NotificationId final int id, @Nullable PendingIntent pi,
+ @NonNull final Action... actions) {
final Notification notification =
new Notification.Builder(mContext, mChannel.getId())
.setSmallIcon(iconId)
@@ -236,6 +384,7 @@
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setCategory(Notification.CATEGORY_STATUS)
.setContentIntent(pi)
+ .setActions(actions)
.build();
mNotificationManager.notify(null /* tag */, id, notification);
diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml
index 55640db..31eaabf 100644
--- a/packages/Tethering/tests/unit/AndroidManifest.xml
+++ b/packages/Tethering/tests/unit/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.networkstack.tethering.tests.unit">
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.TETHER_PRIVILEGED"/>
<application android:debuggable="true">
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
index 7bff74b..5f88588 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
@@ -23,14 +23,26 @@
import android.net.ConnectivityManager.TETHERING_BLUETOOTH
import android.net.ConnectivityManager.TETHERING_USB
import android.net.ConnectivityManager.TETHERING_WIFI
+import android.net.Network
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
import android.os.UserHandle
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
-import androidx.test.platform.app.InstrumentationRegistry
+import android.telephony.TelephonyManager
import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import com.android.internal.util.test.BroadcastInterceptingContext
import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE
+import com.android.networkstack.tethering.TetheringNotificationUpdater.ENABLE_NOTIFICATION_ID
+import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM
+import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID
+import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID
+import com.android.testutils.waitForIdle
+import org.junit.After
import org.junit.Assert.assertEquals
+import org.junit.Assert.fail
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,8 +55,8 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.times
-import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.MockitoAnnotations
const val TEST_SUBID = 1
@@ -55,10 +67,13 @@
const val WIFI_MASK = 1 shl TETHERING_WIFI
const val USB_MASK = 1 shl TETHERING_USB
const val BT_MASK = 1 shl TETHERING_BLUETOOTH
-const val TITTLE = "Tethering active"
+const val TITLE = "Tethering active"
const val MESSAGE = "Tap here to set up."
-const val TEST_TITTLE = "Hotspot active"
+const val TEST_TITLE = "Hotspot active"
const val TEST_MESSAGE = "Tap to set up hotspot."
+const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access"
+const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet."
+const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot"
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -67,12 +82,15 @@
// should crash if they are used before being initialized.
@Mock private lateinit var mockContext: Context
@Mock private lateinit var notificationManager: NotificationManager
+ @Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var defaultResources: Resources
@Mock private lateinit var testResources: Resources
- // lateinit for this class under test, as it should be reset to a different instance for every
- // tests but should always be initialized before use (or the test should crash).
+ // lateinit for these classes under test, as they should be reset to a different instance for
+ // every test but should always be initialized before use (or the test should crash).
+ private lateinit var context: TestContext
private lateinit var notificationUpdater: TetheringNotificationUpdater
+ private lateinit var fakeTetheringThread: HandlerThread
private val ENABLE_ICON_CONFIGS = arrayOf(
"USB;android.test:drawable/usb", "BT;android.test:drawable/bluetooth",
@@ -82,11 +100,19 @@
private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) {
override fun createContextAsUser(user: UserHandle, flags: Int) =
if (user == UserHandle.ALL) mockContext else this
+ override fun getSystemService(name: String) =
+ if (name == Context.TELEPHONY_SERVICE) telephonyManager
+ else super.getSystemService(name)
}
- private inner class WrappedNotificationUpdater(c: Context) : TetheringNotificationUpdater(c) {
+ private inner class WrappedNotificationUpdater(c: Context, looper: Looper)
+ : TetheringNotificationUpdater(c, looper) {
override fun getResourcesForSubId(context: Context, subId: Int) =
- if (subId == TEST_SUBID) testResources else defaultResources
+ when (subId) {
+ TEST_SUBID -> testResources
+ INVALID_SUBSCRIPTION_ID -> defaultResources
+ else -> super.getResourcesForSubId(context, subId)
+ }
}
private fun setupResources() {
@@ -94,12 +120,20 @@
.getStringArray(R.array.tethering_notification_icons)
doReturn(arrayOf("WIFI;android.test:drawable/wifi")).`when`(testResources)
.getStringArray(R.array.tethering_notification_icons)
- doReturn(TITTLE).`when`(defaultResources).getString(R.string.tethering_notification_title)
+ doReturn(5).`when`(testResources)
+ .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
+ doReturn(TITLE).`when`(defaultResources).getString(R.string.tethering_notification_title)
doReturn(MESSAGE).`when`(defaultResources)
.getString(R.string.tethering_notification_message)
- doReturn(TEST_TITTLE).`when`(testResources).getString(R.string.tethering_notification_title)
+ doReturn(TEST_TITLE).`when`(testResources).getString(R.string.tethering_notification_title)
doReturn(TEST_MESSAGE).`when`(testResources)
.getString(R.string.tethering_notification_message)
+ doReturn(TEST_NO_UPSTREAM_TITLE).`when`(testResources)
+ .getString(R.string.no_upstream_notification_title)
+ doReturn(TEST_NO_UPSTREAM_MESSAGE).`when`(testResources)
+ .getString(R.string.no_upstream_notification_message)
+ doReturn(TEST_NO_UPSTREAM_BUTTON).`when`(testResources)
+ .getString(R.string.no_upstream_notification_disable_button)
doReturn(USB_ICON_ID).`when`(defaultResources)
.getIdentifier(eq("android.test:drawable/usb"), any(), any())
doReturn(BT_ICON_ID).`when`(defaultResources)
@@ -113,35 +147,61 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- val context = TestContext(InstrumentationRegistry.getInstrumentation().context)
+ context = TestContext(InstrumentationRegistry.getInstrumentation().context)
doReturn(notificationManager).`when`(mockContext)
.getSystemService(Context.NOTIFICATION_SERVICE)
- notificationUpdater = WrappedNotificationUpdater(context)
+ fakeTetheringThread = HandlerThread(this::class.simpleName)
+ fakeTetheringThread.start()
+ notificationUpdater = WrappedNotificationUpdater(context, fakeTetheringThread.looper)
setupResources()
}
+ @After
+ fun tearDown() {
+ fakeTetheringThread.quitSafely()
+ }
+
private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE)
private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT)
- private fun verifyNotification(iconId: Int = 0, title: String = "", text: String = "") {
- verify(notificationManager, never()).cancel(any(), anyInt())
+ private fun verifyNotification(iconId: Int, title: String, text: String, id: Int) {
+ verify(notificationManager, never()).cancel(any(), eq(id))
val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
verify(notificationManager, times(1))
- .notify(any(), anyInt(), notificationCaptor.capture())
+ .notify(any(), eq(id), notificationCaptor.capture())
val notification = notificationCaptor.getValue()
assertEquals(iconId, notification.smallIcon.resId)
assertEquals(title, notification.title())
assertEquals(text, notification.text())
+ }
+ private fun verifyNotificationCancelled(id: Int) =
+ verify(notificationManager, times(1)).cancel(any(), eq(id))
+
+ private val tetheringActiveNotifications =
+ listOf(NO_UPSTREAM_NOTIFICATION_ID, ENABLE_NOTIFICATION_ID)
+
+ private fun verifyCancelAllTetheringActiveNotifications() {
+ tetheringActiveNotifications.forEach {
+ verifyNotificationCancelled(it)
+ }
reset(notificationManager)
}
- private fun verifyNoNotification() {
- verify(notificationManager, times(1)).cancel(any(), anyInt())
- verify(notificationManager, never()).notify(any(), anyInt(), any())
-
+ private fun verifyOnlyTetheringActiveNotification(
+ notifyId: Int,
+ iconId: Int,
+ title: String,
+ text: String
+ ) {
+ tetheringActiveNotifications.forEach {
+ when (it) {
+ notifyId -> verifyNotification(iconId, title, text, notifyId)
+ else -> verifyNotificationCancelled(it)
+ }
+ }
reset(notificationManager)
}
@@ -149,7 +209,7 @@
fun testNotificationWithDownstreamChanged() {
// Wifi downstream. No notification.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
- verifyNoNotification()
+ verifyCancelAllTetheringActiveNotifications()
// Same downstream changed. Nothing happened.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
@@ -157,22 +217,23 @@
// Wifi and usb downstreams. Show enable notification
notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK)
- verifyNotification(GENERAL_ICON_ID, TITTLE, MESSAGE)
+ verifyOnlyTetheringActiveNotification(
+ ENABLE_NOTIFICATION_ID, GENERAL_ICON_ID, TITLE, MESSAGE)
// Usb downstream. Still show enable notification.
notificationUpdater.onDownstreamChanged(USB_MASK)
- verifyNotification(USB_ICON_ID, TITTLE, MESSAGE)
+ verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE)
// No downstream. No notification.
notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
- verifyNoNotification()
+ verifyCancelAllTetheringActiveNotifications()
}
@Test
fun testNotificationWithActiveDataSubscriptionIdChanged() {
// Usb downstream. Showed enable notification with default resource.
notificationUpdater.onDownstreamChanged(USB_MASK)
- verifyNotification(USB_ICON_ID, TITTLE, MESSAGE)
+ verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE)
// Same subId changed. Nothing happened.
notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID)
@@ -180,15 +241,16 @@
// Set test sub id. Clear notification with test resource.
notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
- verifyNoNotification()
+ verifyCancelAllTetheringActiveNotifications()
// Wifi downstream. Show enable notification with test resource.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
- verifyNotification(WIFI_ICON_ID, TEST_TITTLE, TEST_MESSAGE)
+ verifyOnlyTetheringActiveNotification(
+ ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
// No downstream. No notification.
notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
- verifyNoNotification()
+ verifyCancelAllTetheringActiveNotifications()
}
private fun assertIconNumbers(number: Int, configs: Array<String?>) {
@@ -227,10 +289,8 @@
@Test
fun testSetupRestrictedNotification() {
- val title = InstrumentationRegistry.getInstrumentation().context.resources
- .getString(R.string.disable_tether_notification_title)
- val message = InstrumentationRegistry.getInstrumentation().context.resources
- .getString(R.string.disable_tether_notification_message)
+ val title = context.resources.getString(R.string.disable_tether_notification_title)
+ val message = context.resources.getString(R.string.disable_tether_notification_message)
val disallowTitle = "Tether function is disallowed"
val disallowMessage = "Please contact your admin"
doReturn(title).`when`(defaultResources)
@@ -244,18 +304,127 @@
// User restrictions on. Show restricted notification.
notificationUpdater.notifyTetheringDisabledByRestriction()
- verifyNotification(R.drawable.stat_sys_tether_general, title, message)
+ verifyNotification(R.drawable.stat_sys_tether_general, title, message,
+ RESTRICTED_NOTIFICATION_ID)
+ reset(notificationManager)
// User restrictions off. Clear notification.
notificationUpdater.tetheringRestrictionLifted()
- verifyNoNotification()
+ verifyNotificationCancelled(RESTRICTED_NOTIFICATION_ID)
+ reset(notificationManager)
// Set test sub id. No notification.
notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
- verifyNoNotification()
+ verifyCancelAllTetheringActiveNotifications()
// User restrictions on again. Show restricted notification with test resource.
notificationUpdater.notifyTetheringDisabledByRestriction()
- verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage)
+ verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage,
+ RESTRICTED_NOTIFICATION_ID)
+ reset(notificationManager)
+ }
+
+ val MAX_BACKOFF_MS = 200L
+ /**
+ * Waits for all messages, including delayed ones, to be processed.
+ *
+ * This will wait until the handler has no more messages to be processed including
+ * delayed ones, or the timeout has expired. It uses an exponential backoff strategy
+ * to wait longer and longer to consume less CPU, with the max granularity being
+ * MAX_BACKOFF_MS.
+ *
+ * @return true if all messages have been processed including delayed ones, false if timeout
+ *
+ * TODO: Move this method to com.android.testutils.HandlerUtils.kt.
+ */
+ private fun Handler.waitForDelayedMessage(what: Int?, timeoutMs: Long) {
+ fun hasMatchingMessages() =
+ if (what == null) hasMessagesOrCallbacks() else hasMessages(what)
+ val expiry = System.currentTimeMillis() + timeoutMs
+ var delay = 5L
+ while (System.currentTimeMillis() < expiry && hasMatchingMessages()) {
+ // None of Handler, Looper, Message and MessageQueue expose any way to retrieve
+ // the time when the next (let alone the last) message will be processed, so
+ // short of examining the internals with reflection sleep() is the only solution.
+ Thread.sleep(delay)
+ delay = (delay * 2)
+ .coerceAtMost(expiry - System.currentTimeMillis())
+ .coerceAtMost(MAX_BACKOFF_MS)
+ }
+
+ val timeout = expiry - System.currentTimeMillis()
+ if (timeout <= 0) fail("Delayed message did not process yet after ${timeoutMs}ms")
+ waitForIdle(timeout)
+ }
+
+ @Test
+ fun testNotificationWithUpstreamNetworkChanged() {
+ // Set test sub id. No notification.
+ notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
+ verifyCancelAllTetheringActiveNotifications()
+
+ // Wifi downstream. Show enable notification with test resource.
+ notificationUpdater.onDownstreamChanged(WIFI_MASK)
+ verifyOnlyTetheringActiveNotification(
+ ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
+
+ // There is no upstream. Show no upstream notification.
+ notificationUpdater.onUpstreamNetworkChanged(null)
+ notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
+ verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE,
+ TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID)
+ reset(notificationManager)
+
+ // Same upstream network changed. Nothing happened.
+ notificationUpdater.onUpstreamNetworkChanged(null)
+ verifyZeroInteractions(notificationManager)
+
+ // Upstream come back. Clear no upstream notification.
+ notificationUpdater.onUpstreamNetworkChanged(Network(1000))
+ verifyNotificationCancelled(NO_UPSTREAM_NOTIFICATION_ID)
+ reset(notificationManager)
+
+ // No upstream again. Show no upstream notification.
+ notificationUpdater.onUpstreamNetworkChanged(null)
+ notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
+ verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE,
+ TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID)
+ reset(notificationManager)
+
+ // No downstream. No notification.
+ notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
+ verifyCancelAllTetheringActiveNotifications()
+
+ // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to 0 and have wifi downstream
+ // again. Show enable notification only.
+ doReturn(-1).`when`(testResources)
+ .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
+ notificationUpdater.onDownstreamChanged(WIFI_MASK)
+ notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
+ verifyOnlyTetheringActiveNotification(
+ ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
+ }
+
+ @Test
+ fun testGetResourcesForSubId() {
+ doReturn(telephonyManager).`when`(telephonyManager).createForSubscriptionId(anyInt())
+ doReturn(1234).`when`(telephonyManager).getSimCarrierId()
+ doReturn("000000").`when`(telephonyManager).getSimOperator()
+
+ val subId = -2 // Use invalid subId to avoid getting resource from cache or real subId.
+ val config = context.resources.configuration
+ var res = notificationUpdater.getResourcesForSubId(context, subId)
+ assertEquals(config.mcc, res.configuration.mcc)
+ assertEquals(config.mnc, res.configuration.mnc)
+
+ doReturn(1839).`when`(telephonyManager).getSimCarrierId()
+ res = notificationUpdater.getResourcesForSubId(context, subId)
+ assertEquals(config.mcc, res.configuration.mcc)
+ assertEquals(config.mnc, res.configuration.mnc)
+
+ doReturn("20404").`when`(telephonyManager).getSimOperator()
+ res = notificationUpdater.getResourcesForSubId(context, subId)
+ assertEquals(311, res.configuration.mcc)
+ assertEquals(480, res.configuration.mnc)
}
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index d4be3a2..feb99e6 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -383,7 +383,7 @@
}
@Override
- public TetheringNotificationUpdater getNotificationUpdater(Context ctx) {
+ public TetheringNotificationUpdater getNotificationUpdater(Context ctx, Looper looper) {
return mNotificationUpdater;
}
}
@@ -1691,6 +1691,18 @@
assertEquals(clientAddrParceled, params.clientAddr);
}
+ @Test
+ public void testUpstreamNetworkChanged() {
+ final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM)
+ mTetheringDependencies.mUpstreamNetworkMonitorMasterSM;
+ final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
+ when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState);
+ stateMachine.chooseUpstreamType(true);
+
+ verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network));
+ verify(mNotificationUpdater, times(1)).onUpstreamNetworkChanged(eq(upstreamState.network));
+ }
+
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7230b00..d252f9e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2441,7 +2441,11 @@
* accessibility button.
* 2) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk
* version <= Q: turns on / off the accessibility service.
- * 3) For services targeting sdk version > Q:
+ * 3) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk
+ * version > Q and request accessibility button: turn on the accessibility service if it's
+ * not in the enabled state.
+ * (It'll happen when a service is disabled and assigned to shortcut then upgraded.)
+ * 4) For services targeting sdk version > Q:
* a) Turns on / off the accessibility service, if service does not request accessibility
* button.
* b) Callbacks to accessibility service if service is bounded and requests accessibility
@@ -2475,6 +2479,13 @@
}
return true;
}
+ if (shortcutType == ACCESSIBILITY_SHORTCUT_KEY && targetSdk > Build.VERSION_CODES.Q
+ && requestA11yButton) {
+ if (!userState.getEnabledServicesLocked().contains(assignedTarget)) {
+ enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
+ return true;
+ }
+ }
// Callbacks to a11y service if it's bounded and requests a11y button.
if (serviceConnection == null
|| !userState.mBoundServices.contains(serviceConnection)
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3d68618..9d1ad42 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2653,6 +2653,11 @@
} else if (viewState.id.equals(this.mCurrentViewId)
&& (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) {
requestShowInlineSuggestionsLocked(viewState.getResponse(), filterText);
+ } else if (viewState.id.equals(this.mCurrentViewId)
+ && (viewState.getState() & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) {
+ if (!TextUtils.isEmpty(filterText)) {
+ mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
+ }
}
viewState.setState(ViewState.STATE_CHANGED);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 31bccea..8ecda8f1 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2830,7 +2830,6 @@
private int checkOperationImpl(int code, int uid, String packageName,
boolean raw) {
- verifyIncomingUid(uid);
verifyIncomingOp(code);
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index a87fb8b..b4f7cdb 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -552,7 +552,8 @@
}
if (state == Display.STATE_OFF) {
brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT;
- } else if (brightnessState < PowerManager.BRIGHTNESS_MIN || Float.isNaN(brightnessState)) {
+ } else if (brightnessState != PowerManager.BRIGHTNESS_OFF_FLOAT
+ && brightnessState < PowerManager.BRIGHTNESS_MIN) {
brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
} else if (brightnessState > PowerManager.BRIGHTNESS_MAX) {
brightnessState = PowerManager.BRIGHTNESS_MAX;
diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java
index 195b059..095cd14 100644
--- a/services/core/java/com/android/server/location/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/GeofenceManager.java
@@ -253,7 +253,7 @@
int op = CallerIdentity.asAppOp(identity.permissionLevel);
if (op >= 0) {
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, identity.uid,
- identity.packageName, identity.featureId, null)
+ identity.packageName, identity.featureId, identity.listenerId)
!= AppOpsManager.MODE_ALLOWED) {
continue;
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6e2feeb..1345e37 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -261,8 +261,10 @@
.build();
builder.addSelectedRoute(mSelectedRouteId);
- for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
- builder.addTransferableRoute(route.getId());
+ if (mBtRouteProvider != null) {
+ for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
+ builder.addTransferableRoute(route.getId());
+ }
}
RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index aed2927..e98326b 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -30,6 +30,7 @@
import android.service.notification.Condition;
import android.service.notification.ConditionProviderService;
import android.service.notification.IConditionProvider;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -54,7 +55,6 @@
private final ArraySet<String> mSystemConditionProviderNames;
private final ArraySet<SystemConditionProviderService> mSystemConditionProviders
= new ArraySet<>();
-
private Callback mCallback;
public ConditionProviders(Context context, UserProfiles userProfiles, IPackageManager pm) {
@@ -195,6 +195,21 @@
}
@Override
+ protected void loadDefaultsFromConfig() {
+ String defaultDndAccess = mContext.getResources().getString(
+ R.string.config_defaultDndAccessPackages);
+ if (defaultDndAccess != null) {
+ String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
+ for (int i = 0; i < dnds.length; i++) {
+ if (TextUtils.isEmpty(dnds[i])) {
+ continue;
+ }
+ addDefaultComponentOrPackage(dnds[i]);
+ }
+ }
+ }
+
+ @Override
protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
if (removed == null) return;
for (int i = mRecords.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 45df368..5d3dc5f 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -21,6 +21,7 @@
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.DEVICE_POLICY_SERVICE;
import static android.os.UserHandle.USER_ALL;
+import static android.os.UserHandle.USER_SYSTEM;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -96,6 +97,8 @@
private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
protected static final String ENABLED_SERVICES_SEPARATOR = ":";
+ private static final String DB_VERSION_1 = "1";
+
/**
* List of components and apps that can have running {@link ManagedServices}.
@@ -107,7 +110,7 @@
static final String ATT_VERSION = "version";
static final String ATT_DEFAULTS = "defaults";
- static final int DB_VERSION = 1;
+ static final int DB_VERSION = 2;
static final int APPROVAL_BY_PACKAGE = 0;
static final int APPROVAL_BY_COMPONENT = 1;
@@ -187,17 +190,22 @@
protected void addDefaultComponentOrPackage(String packageOrComponent) {
if (!TextUtils.isEmpty(packageOrComponent)) {
synchronized (mDefaultsLock) {
- ComponentName cn = ComponentName.unflattenFromString(packageOrComponent);
- if (cn == null) {
+ if (mApprovalLevel == APPROVAL_BY_PACKAGE) {
mDefaultPackages.add(packageOrComponent);
- } else {
+ return;
+ }
+ ComponentName cn = ComponentName.unflattenFromString(packageOrComponent);
+ if (cn != null && mApprovalLevel == APPROVAL_BY_COMPONENT) {
mDefaultPackages.add(cn.getPackageName());
mDefaultComponents.add(cn);
+ return;
}
}
}
}
+ protected abstract void loadDefaultsFromConfig();
+
boolean isDefaultComponentOrPackage(String packageOrComponent) {
synchronized (mDefaultsLock) {
ComponentName cn = ComponentName.unflattenFromString(packageOrComponent);
@@ -504,19 +512,19 @@
void readDefaults(XmlPullParser parser) {
String defaultComponents = XmlUtils.readStringAttribute(parser, ATT_DEFAULTS);
- if (defaultComponents == null) {
- return;
- }
- String[] components = defaultComponents.split(ENABLED_SERVICES_SEPARATOR);
- synchronized (mDefaultsLock) {
- for (int i = 0; i < components.length; i++) {
- if (!TextUtils.isEmpty(components[i])) {
- ComponentName cn = ComponentName.unflattenFromString(components[i]);
- if (cn != null) {
- mDefaultPackages.add(cn.getPackageName());
- mDefaultComponents.add(cn);
- } else {
- mDefaultPackages.add(components[i]);
+
+ if (!TextUtils.isEmpty(defaultComponents)) {
+ String[] components = defaultComponents.split(ENABLED_SERVICES_SEPARATOR);
+ synchronized (mDefaultsLock) {
+ for (int i = 0; i < components.length; i++) {
+ if (!TextUtils.isEmpty(components[i])) {
+ ComponentName cn = ComponentName.unflattenFromString(components[i]);
+ if (cn != null) {
+ mDefaultPackages.add(cn.getPackageName());
+ mDefaultComponents.add(cn);
+ } else {
+ mDefaultPackages.add(components[i]);
+ }
}
}
}
@@ -531,9 +539,11 @@
throws XmlPullParserException, IOException {
// read grants
int type;
+ String version = "";
readDefaults(parser);
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
String tag = parser.getName();
+ version = XmlUtils.readStringAttribute(parser, ATT_VERSION);
if (type == XmlPullParser.END_TAG
&& getConfig().xmlTag.equals(tag)) {
break;
@@ -561,9 +571,38 @@
}
}
}
+ boolean isVersionOne = TextUtils.isEmpty(version) || DB_VERSION_1.equals(version);
+ if (isVersionOne) {
+ upgradeToVersionTwo();
+ }
rebindServices(false, USER_ALL);
}
+ private void upgradeToVersionTwo() {
+ // check if any defaults are loaded
+ int defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
+ if (defaultsSize == 0) {
+ // load defaults from current allowed
+ if (this.mApprovalLevel == APPROVAL_BY_COMPONENT) {
+ List<ComponentName> approvedComponents = getAllowedComponents(USER_SYSTEM);
+ for (int i = 0; i < approvedComponents.size(); i++) {
+ addDefaultComponentOrPackage(approvedComponents.get(i).flattenToString());
+ }
+ }
+ if (this.mApprovalLevel == APPROVAL_BY_PACKAGE) {
+ List<String> approvedPkgs = getAllowedPackages(USER_SYSTEM);
+ for (int i = 0; i < approvedPkgs.size(); i++) {
+ addDefaultComponentOrPackage(approvedPkgs.get(i));
+ }
+ }
+ }
+ // if no defaults are loaded, then load from config
+ defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
+ if (defaultsSize == 0) {
+ loadDefaultsFromConfig();
+ }
+ }
+
/**
* Read extra attributes in the {@link #TAG_MANAGED_SERVICES} tag.
*/
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 8eb41ad..9b02b48 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -600,57 +600,11 @@
}
void loadDefaultApprovedServices(int userId) {
- String defaultListenerAccess = getContext().getResources().getString(
- com.android.internal.R.string.config_defaultListenerAccessPackages);
- if (defaultListenerAccess != null) {
- String[] listeners =
- defaultListenerAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
- for (int i = 0; i < listeners.length; i++) {
- if (TextUtils.isEmpty(listeners[i])) {
- continue;
- }
- ArraySet<ComponentName> approvedListeners =
- mListeners.queryPackageForServices(listeners[i],
- MATCH_DIRECT_BOOT_AWARE
- | MATCH_DIRECT_BOOT_UNAWARE, userId);
- for (int k = 0; k < approvedListeners.size(); k++) {
- ComponentName cn = approvedListeners.valueAt(k);
- mListeners.addDefaultComponentOrPackage(cn.flattenToString());
- }
- }
- }
+ mListeners.loadDefaultsFromConfig();
- String defaultDndAccess = getContext().getResources().getString(
- com.android.internal.R.string.config_defaultDndAccessPackages);
- if (defaultDndAccess != null) {
- String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
- for (int i = 0; i < dnds.length; i++) {
- if (TextUtils.isEmpty(dnds[i])) {
- continue;
- }
- mConditionProviders.addDefaultComponentOrPackage(dnds[i]);
- }
- }
+ mConditionProviders.loadDefaultsFromConfig();
-
- ArraySet<String> assistants = new ArraySet<>();
- String deviceAssistant = DeviceConfig.getProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE);
- if (deviceAssistant != null) {
- assistants.addAll(Arrays.asList(deviceAssistant.split(
- ManagedServices.ENABLED_SERVICES_SEPARATOR)));
- }
- assistants.addAll(Arrays.asList(getContext().getResources().getString(
- com.android.internal.R.string.config_defaultAssistantAccessComponent)
- .split(ManagedServices.ENABLED_SERVICES_SEPARATOR)));
- for (int i = 0; i < assistants.size(); i++) {
- String cnString = assistants.valueAt(i);
- if (TextUtils.isEmpty(cnString)) {
- continue;
- }
- mAssistants.addDefaultComponentOrPackage(cnString);
- }
+ mAssistants.loadDefaultsFromConfig();
}
protected void allowDefaultApprovedServices(int userId) {
@@ -673,11 +627,14 @@
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE);
if (overrideDefaultAssistantString != null) {
- ComponentName overrideDefaultAssistant =
- ComponentName.unflattenFromString(overrideDefaultAssistantString);
- if (allowAssistant(userId, overrideDefaultAssistant)) return;
+ ArraySet<ComponentName> approved = mAssistants.queryPackageForServices(
+ overrideDefaultAssistantString,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+ userId);
+ for (int i = 0; i < approved.size(); i++) {
+ if (allowAssistant(userId, approved.valueAt(i))) return;
+ }
}
-
ArraySet<ComponentName> defaults = mAssistants.getDefaultComponents();
// We should have only one default assistant by default
// allowAssistant should execute once in practice
@@ -8603,6 +8560,26 @@
private ArrayMap<Integer, Boolean> mUserSetMap = new ArrayMap<>();
private Set<String> mAllowedAdjustments = new ArraySet<>();
+ @Override
+ protected void loadDefaultsFromConfig() {
+ ArraySet<String> assistants = new ArraySet<>();
+ assistants.addAll(Arrays.asList(mContext.getResources().getString(
+ com.android.internal.R.string.config_defaultAssistantAccessComponent)
+ .split(ManagedServices.ENABLED_SERVICES_SEPARATOR)));
+ for (int i = 0; i < assistants.size(); i++) {
+ String cnString = assistants.valueAt(i);
+ if (TextUtils.isEmpty(cnString)) {
+ continue;
+ }
+ ArraySet<ComponentName> approved = queryPackageForServices(cnString,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, USER_SYSTEM);
+ for (int k = 0; k < approved.size(); k++) {
+ ComponentName cn = approved.valueAt(k);
+ addDefaultComponentOrPackage(cn.flattenToString());
+ }
+ }
+ }
+
public NotificationAssistants(Context context, Object lock, UserProfiles up,
IPackageManager pm) {
super(context, lock, up, pm);
@@ -9040,7 +9017,29 @@
public NotificationListeners(IPackageManager pm) {
super(getContext(), mNotificationLock, mUserProfiles, pm);
+ }
+ @Override
+ protected void loadDefaultsFromConfig() {
+ String defaultListenerAccess = mContext.getResources().getString(
+ R.string.config_defaultListenerAccessPackages);
+ if (defaultListenerAccess != null) {
+ String[] listeners =
+ defaultListenerAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
+ for (int i = 0; i < listeners.length; i++) {
+ if (TextUtils.isEmpty(listeners[i])) {
+ continue;
+ }
+ ArraySet<ComponentName> approvedListeners =
+ this.queryPackageForServices(listeners[i],
+ MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE, USER_SYSTEM);
+ for (int k = 0; k < approvedListeners.size(); k++) {
+ ComponentName cn = approvedListeners.valueAt(k);
+ addDefaultComponentOrPackage(cn.flattenToString());
+ }
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 9fb468e..7cee286 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -71,6 +71,8 @@
public static final int DEXOPT_GENERATE_COMPACT_DEX = 1 << 11;
/** Indicates that dexopt should generate an app image */
public static final int DEXOPT_GENERATE_APP_IMAGE = 1 << 12;
+ /** Indicates that dexopt may be run with different performance / priority tuned for restore */
+ public static final int DEXOPT_FOR_RESTORE = 1 << 13; // TODO(b/135202722): remove
public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE;
public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE;
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 1951e74..4b8a242 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -22,6 +22,7 @@
import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
import static com.android.server.pm.Installer.DEXOPT_FORCE;
+import static com.android.server.pm.Installer.DEXOPT_FOR_RESTORE;
import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
@@ -706,6 +707,7 @@
| (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0)
| (generateCompactDex ? DEXOPT_GENERATE_COMPACT_DEX : 0)
| (generateAppImage ? DEXOPT_GENERATE_APP_IMAGE : 0)
+ | (options.isDexoptInstallForRestore() ? DEXOPT_FOR_RESTORE : 0)
| hiddenApiFlag;
return adjustDexoptFlags(dexFlags);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 12ee2f5..d2481b7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -66,6 +66,8 @@
import static android.content.pm.PackageManager.INSTALL_INTERNAL;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
+import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
@@ -3579,7 +3581,8 @@
// Prepare a supplier of package parser for the staging manager to parse apex file
// during the staging installation.
final Supplier<PackageParser2> apexParserSupplier = () -> new PackageParser2(
- mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mPackageParserCallback);
+ mSeparateProcesses, mOnlyCore, mMetrics, null /* cacheDir */,
+ mPackageParserCallback);
mInstallerService = new PackageInstallerService(mContext, this, apexParserSupplier);
final Pair<ComponentName, String> instantAppResolverComponent =
getInstantAppResolverLPr();
@@ -16709,10 +16712,15 @@
// method because `pkg` may not be in `mPackages` yet.
//
// Also, don't fail application installs if the dexopt step fails.
+ int flags = DexoptOptions.DEXOPT_BOOT_COMPLETE
+ | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
+ if (reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_RESTORE
+ || reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_SETUP) {
+ flags |= DexoptOptions.DEXOPT_FOR_RESTORE;
+ }
DexoptOptions dexoptOptions = new DexoptOptions(packageName,
REASON_INSTALL,
- DexoptOptions.DEXOPT_BOOT_COMPLETE
- | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
+ flags);
ScanResult result = reconciledPkg.scanResult;
// This mirrors logic from commitReconciledScanResultLocked, where the library files
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index c0502b8..0b6024a 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -211,24 +211,16 @@
UserManager.DISALLOW_ADD_USER,
UserManager.DISALLOW_BLUETOOTH,
UserManager.DISALLOW_BLUETOOTH_SHARING,
- UserManager.DISALLOW_CONFIG_BLUETOOTH,
UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
- UserManager.DISALLOW_CONFIG_LOCATION,
UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
UserManager.DISALLOW_CONFIG_TETHERING,
- UserManager.DISALLOW_CONFIG_WIFI,
- UserManager.DISALLOW_CONTENT_CAPTURE,
- UserManager.DISALLOW_CONTENT_SUGGESTIONS,
UserManager.DISALLOW_DATA_ROAMING,
- UserManager.DISALLOW_DEBUGGING_FEATURES,
UserManager.DISALLOW_SAFE_BOOT,
- UserManager.DISALLOW_SHARE_LOCATION,
UserManager.DISALLOW_SMS,
UserManager.DISALLOW_USB_FILE_TRANSFER,
UserManager.DISALLOW_AIRPLANE_MODE,
UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
- UserManager.DISALLOW_OUTGOING_CALLS,
UserManager.DISALLOW_UNMUTE_MICROPHONE
);
@@ -237,7 +229,16 @@
* set on the parent profile instance to apply them on the personal profile.
*/
private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_LOCAL_RESTRICTIONS =
- Sets.newArraySet();
+ Sets.newArraySet(
+ UserManager.DISALLOW_CONFIG_BLUETOOTH,
+ UserManager.DISALLOW_CONFIG_LOCATION,
+ UserManager.DISALLOW_CONFIG_WIFI,
+ UserManager.DISALLOW_CONTENT_CAPTURE,
+ UserManager.DISALLOW_CONTENT_SUGGESTIONS,
+ UserManager.DISALLOW_DEBUGGING_FEATURES,
+ UserManager.DISALLOW_SHARE_LOCATION,
+ UserManager.DISALLOW_OUTGOING_CALLS
+ );
/**
* User restrictions that default to {@code true} for managed profile owners.
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index b453c89..68f3886 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -61,6 +61,10 @@
// should get the dex metdata file if present.
public static final int DEXOPT_INSTALL_WITH_DEX_METADATA_FILE = 1 << 10;
+ // When set, indicates that dexopt is being invoked from the install flow during device restore
+ // or device setup and should be scheduled appropriately.
+ public static final int DEXOPT_FOR_RESTORE = 1 << 11; // TODO(b/135202722): remove
+
// The name of package to optimize.
private final String mPackageName;
@@ -99,7 +103,8 @@
DEXOPT_DOWNGRADE |
DEXOPT_AS_SHARED_LIBRARY |
DEXOPT_IDLE_BACKGROUND_JOB |
- DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
+ DEXOPT_INSTALL_WITH_DEX_METADATA_FILE |
+ DEXOPT_FOR_RESTORE;
if ((flags & (~validityMask)) != 0) {
throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
}
@@ -155,6 +160,10 @@
return (mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) != 0;
}
+ public boolean isDexoptInstallForRestore() {
+ return (mFlags & DEXOPT_FOR_RESTORE) != 0;
+ }
+
public String getSplitName() {
return mSplitName;
}
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 244364e..bacc7ac 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -26,6 +26,7 @@
import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
@@ -1656,7 +1657,8 @@
final int userSettableMask = FLAG_PERMISSION_USER_SET
| FLAG_PERMISSION_USER_FIXED
| FLAG_PERMISSION_REVOKED_COMPAT
- | FLAG_PERMISSION_REVIEW_REQUIRED;
+ | FLAG_PERMISSION_REVIEW_REQUIRED
+ | FLAG_PERMISSION_ONE_TIME;
final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED
| FLAG_PERMISSION_POLICY_FIXED;
diff --git a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
index 67677c6..e1e6195 100644
--- a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
@@ -41,10 +41,11 @@
public static MemorySnapshot readMemorySnapshotFromProcfs(int pid) {
long[] output = new long[STATUS_KEYS.length];
output[0] = -1;
+ output[3] = -1;
+ output[4] = -1;
Process.readProcLines("/proc/" + pid + "/status", STATUS_KEYS, output);
- if (output[0] == -1 || (output[3] == 0 && output[4] == 0)) {
- // Could not open file or anon rss / swap are 0 indicating the process is in a zombie
- // state.
+ if (output[0] == -1 || output[3] == -1 || output[4] == -1) {
+ // Could not open or parse file.
return null;
}
final MemorySnapshot snapshot = new MemorySnapshot();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e79b804..521ffa5 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1374,8 +1374,8 @@
final Rect spaceToFill = transformedBounds != null
? transformedBounds
: inMultiWindowMode()
- ? task.getDisplayedBounds()
- : getRootTask().getParent().getDisplayedBounds();
+ ? task.getBounds()
+ : getRootTask().getParent().getBounds();
mLetterbox.layout(spaceToFill, w.getFrameLw(), mTmpPoint);
} else if (mLetterbox != null) {
mLetterbox.hide();
@@ -6663,17 +6663,6 @@
return super.getBounds();
}
- @Override
- Rect getDisplayedBounds() {
- if (task != null) {
- final Rect overrideDisplayedBounds = task.getOverrideDisplayedBounds();
- if (!overrideDisplayedBounds.isEmpty()) {
- return overrideDisplayedBounds;
- }
- }
- return getBounds();
- }
-
@VisibleForTesting
@Override
Rect getAnimationBounds(int appStackClipMode) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 8bf46bc..5968eede 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -91,7 +91,6 @@
import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
-import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
import static com.android.server.wm.TaskProto.DISPLAY_ID;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
@@ -660,8 +659,7 @@
setBounds(newBounds);
} else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
// For pinned stack, resize is now part of the {@link WindowContainerTransaction}
- resize(new Rect(newBounds), null /* configBounds */,
- PRESERVE_WINDOWS, true /* deferResume */);
+ resize(new Rect(newBounds), PRESERVE_WINDOWS, true /* deferResume */);
}
}
if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
@@ -835,8 +833,7 @@
}
if (!Objects.equals(getRequestedOverrideBounds(), mTmpRect2)) {
- resize(mTmpRect2, null /*configBounds*/,
- false /*preserveWindows*/, true /*deferResume*/);
+ resize(mTmpRect2, false /*preserveWindows*/, true /*deferResume*/);
}
} finally {
mAtmService.continueWindowLayout();
@@ -894,9 +891,6 @@
setTaskBounds(mDeferredBounds);
setBounds(mDeferredBounds);
}
- if (mUpdateDisplayedBoundsDeferredCalled) {
- setTaskDisplayedBounds(mDeferredDisplayedBounds);
- }
}
}
@@ -2966,8 +2960,7 @@
// TODO: Can only be called from special methods in ActivityStackSupervisor.
// Need to consolidate those calls points into this resize method so anyone can call directly.
- void resize(Rect displayedBounds, Rect configBounds, boolean preserveWindows,
- boolean deferResume) {
+ void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) {
if (!updateBoundsAllowed(displayedBounds)) {
return;
}
@@ -2979,7 +2972,7 @@
// Update override configurations of all tasks in the stack.
final PooledConsumer c = PooledLambda.obtainConsumer(
ActivityStack::processTaskResizeBounds, PooledLambda.__(Task.class),
- displayedBounds, configBounds);
+ displayedBounds);
forAllTasks(c, true /* traverseTopToBottom */);
c.recycle();
@@ -3000,17 +2993,10 @@
}
}
- private static void processTaskResizeBounds(
- Task task, Rect displayedBounds, Rect configBounds) {
+ private static void processTaskResizeBounds(Task task, Rect displayedBounds) {
if (!task.isResizeable()) return;
- if (configBounds != null && !configBounds.isEmpty()) {
- task.setOverrideDisplayedBounds(displayedBounds);
- task.setBounds(configBounds);
- } else {
- task.setOverrideDisplayedBounds(null);
- task.setBounds(displayedBounds);
- }
+ task.setBounds(displayedBounds);
}
/**
@@ -3032,22 +3018,6 @@
task.setBounds(task.isResizeable() ? bounds : null);
}
- /** Helper to setDisplayedBounds on all child tasks */
- private void setTaskDisplayedBounds(Rect bounds) {
- if (!updateDisplayedBoundsAllowed(bounds)) {
- return;
- }
-
- final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskDisplayedBounds,
- PooledLambda.__(Task.class), bounds);
- forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
- }
-
- private static void setTaskDisplayedBounds(Task task, Rect bounds) {
- task.setOverrideDisplayedBounds(bounds == null || bounds.isEmpty() ? null : bounds);
- }
-
/**
* Returns the top-most activity that occludes the given one, or @{code null} if none.
*/
@@ -3569,8 +3539,8 @@
}
@Override
- void getRelativeDisplayedPosition(Point outPos) {
- super.getRelativeDisplayedPosition(outPos);
+ void getRelativePosition(Point outPos) {
+ super.getRelativePosition(outPos);
final int outset = getStackOutset();
outPos.x -= outset;
outPos.y -= outset;
@@ -3581,7 +3551,7 @@
return;
}
- final Rect stackBounds = getDisplayedBounds();
+ final Rect stackBounds = getBounds();
int width = stackBounds.width();
int height = stackBounds.height();
@@ -3776,7 +3746,6 @@
proto.write(FILLS_PARENT, matchParentBounds());
getRawBounds().dumpDebug(proto, BOUNDS);
- getOverrideDisplayedBounds().dumpDebug(proto, DISPLAYED_BOUNDS);
if (mLastNonFullscreenBounds != null) {
mLastNonFullscreenBounds.dumpDebug(proto, LAST_NON_FULLSCREEN_BOUNDS);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index f924bd4..3bccced 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1357,7 +1357,7 @@
// still need moveTaskToFrontLocked() below for any transition settings.
}
if (stack.shouldResizeStackWithLaunchBounds()) {
- stack.resize(bounds, null /* configBounds */, !PRESERVE_WINDOWS, !DEFER_RESUME);
+ stack.resize(bounds, !PRESERVE_WINDOWS, !DEFER_RESUME);
} else {
// WM resizeTask must be done after the task is moved to the correct stack,
// because Task's setBounds() also updates dim layer's bounds, but that has
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7a04894..d92f43b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2786,6 +2786,11 @@
}
final int prevMode = task.getWindowingMode();
+ if (prevMode == windowingMode) {
+ // The task is already in split-screen and with correct windowing mode.
+ return true;
+ }
+
moveTaskToSplitScreenPrimaryTask(task, toTop);
return prevMode != task.getWindowingMode();
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2a676e1..a47cdc6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -26,6 +26,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -141,6 +142,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.ScreenOrientation;
@@ -3370,34 +3372,18 @@
}
}
+ private boolean isImeControlledByApp() {
+ return mInputMethodTarget != null && !WindowConfiguration.isSplitScreenWindowingMode(
+ mInputMethodTarget.getWindowingMode());
+ }
+
boolean isImeAttachedToApp() {
- return (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null
+ return isImeControlledByApp()
+ && mInputMethodTarget.mActivityRecord != null
&& mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
// An activity with override bounds should be letterboxed inside its parent bounds,
// so it doesn't fill the screen.
- && mInputMethodTarget.mActivityRecord.matchParentBounds());
- }
-
- /**
- * Get IME target that should host IME when this display that is reparented to another
- * WindowState.
- * IME is never displayed in a child display.
- * Use {@link WindowState#getImeControlTarget()} when IME target window
- * which originally called
- * {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} is known.
- *
- * @return {@link WindowState} of host that controls IME.
- * {@code null} when {@param dc} is not a virtual display.
- * @see DisplayContent#reparent
- */
- @Nullable
- WindowState getImeControlTarget() {
- WindowState imeTarget = mInputMethodTarget;
- if (imeTarget != null) {
- return imeTarget.getImeControlTarget();
- }
-
- return getInsetsStateController().getImeSourceProvider().getControlTarget().getWindow();
+ && mInputMethodTarget.mActivityRecord.matchParentBounds();
}
/**
@@ -3407,7 +3393,6 @@
*
* @param target current IME target.
* @return {@link WindowState} that can host IME.
- * @see DisplayContent#getImeControlTarget()
*/
WindowState getImeHostOrFallback(WindowState target) {
if (target != null && target.getDisplayContent().canShowIme()) {
@@ -3440,7 +3425,7 @@
mInputMethodTarget = target;
mInputMethodTargetWaitingAnim = targetWaitingAnim;
- assignWindowLayers(false /* setLayoutNeeded */);
+ assignWindowLayers(true /* setLayoutNeeded */);
updateImeParent();
updateImeControlTarget();
}
@@ -3448,8 +3433,6 @@
/**
* The IME input target is the window which receives input from IME. It is also a candidate
* which controls the visibility and animation of the input method window.
- *
- * @param target the window that receives input from IME.
*/
void setInputMethodInputTarget(WindowState target) {
if (mInputMethodInputTarget != target) {
@@ -3459,12 +3442,7 @@
}
private void updateImeControlTarget() {
- if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) {
- mInputMethodControlTarget = mRemoteInsetsControlTarget;
- } else {
- // Otherwise, we just use the ime input target
- mInputMethodControlTarget = mInputMethodInputTarget;
- }
+ mInputMethodControlTarget = computeImeControlTarget();
mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
}
@@ -3477,6 +3455,19 @@
}
/**
+ * Computes the window where we hand IME control to.
+ */
+ @VisibleForTesting
+ InsetsControlTarget computeImeControlTarget() {
+ if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null) {
+ return mRemoteInsetsControlTarget;
+ } else {
+ // Otherwise, we just use the ime target as received from IME.
+ return mInputMethodInputTarget;
+ }
+ }
+
+ /**
* Computes the window the IME should be attached to.
*/
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index ba14d48..4ac319d 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -171,7 +171,7 @@
if (aboveIme) {
state = new InsetsState(state);
- state.removeSource(ITYPE_IME);
+ state.setSourceVisible(ITYPE_IME, false);
}
return state;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 0a9878d..51053b2 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -165,17 +165,6 @@
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity");
- // TODO(multi-display) currently only support recents animation in default display.
- final DisplayContent dc =
- mService.mRootWindowContainer.getDefaultDisplay().mDisplayContent;
- if (!mWindowManager.canStartRecentsAnimation()) {
- notifyAnimationCancelBeforeStart(recentsAnimationRunner);
- ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
- "Can't start recents animation, nextAppTransition=%s",
- dc.mAppTransition.getAppTransition());
- return;
- }
-
// If the activity is associated with the recents stack, then try and get that first
ActivityStack targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 54210ae..2ce10a7 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -898,11 +898,11 @@
TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
mTask = task;
mIsRecentTaskInvisible = isRecentTaskInvisible;
- mBounds.set(mTask.getDisplayedBounds());
+ mBounds.set(mTask.getBounds());
mLocalBounds.set(mBounds);
Point tmpPos = new Point();
- mTask.getRelativeDisplayedPosition(tmpPos);
+ mTask.getRelativePosition(tmpPos);
mLocalBounds.offsetTo(tmpPos.x, tmpPos.y);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ad1a205..1ffa01c 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -52,7 +52,6 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
@@ -353,10 +352,6 @@
final Rect mPreparedFrozenBounds = new Rect();
final Configuration mPreparedFrozenMergedConfig = new Configuration();
- // If non-empty, bounds used to display the task during animations/interactions.
- // TODO(b/119687367): This member is temporary.
- private final Rect mOverrideDisplayedBounds = new Rect();
-
// Id of the previous display the stack was on.
int mPrevDisplayId = INVALID_DISPLAY;
@@ -2795,29 +2790,6 @@
}
}
- /**
- * Displayed bounds are used to set where the task is drawn at any given time. This is
- * separate from its actual bounds so that the app doesn't see any meaningful configuration
- * changes during transitionary periods.
- */
- void setOverrideDisplayedBounds(Rect overrideDisplayedBounds) {
- if (overrideDisplayedBounds != null) {
- adjustForMinimalTaskDimensions(overrideDisplayedBounds, mOverrideDisplayedBounds);
- mOverrideDisplayedBounds.set(overrideDisplayedBounds);
- } else {
- mOverrideDisplayedBounds.setEmpty();
- }
- updateSurfacePosition();
- }
-
- /**
- * Gets the bounds that override where the task is displayed. See
- * {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
- */
- Rect getOverrideDisplayedBounds() {
- return mOverrideDisplayedBounds;
- }
-
boolean isResizeable(boolean checkSupportsPip) {
return (mAtmService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
|| (checkSupportsPip && mSupportsPictureInPicture));
@@ -2851,49 +2823,6 @@
mPreparedFrozenMergedConfig.setTo(getConfiguration());
}
- /**
- * Align the task to the adjusted bounds.
- *
- * @param adjustedBounds Adjusted bounds to which the task should be aligned.
- * @param tempInsetBounds Insets bounds for the task.
- * @param alignBottom True if the task's bottom should be aligned to the adjusted
- * bounds's bottom; false if the task's top should be aligned
- * the adjusted bounds's top.
- */
- void alignToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom) {
- if (!isResizeable() || EMPTY.equals(getRequestedOverrideConfiguration())) {
- return;
- }
-
- getBounds(mTmpRect2);
- if (alignBottom) {
- int offsetY = adjustedBounds.bottom - mTmpRect2.bottom;
- mTmpRect2.offset(0, offsetY);
- } else {
- mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
- }
- if (tempInsetBounds == null || tempInsetBounds.isEmpty()) {
- setOverrideDisplayedBounds(null);
- setBounds(mTmpRect2);
- } else {
- setOverrideDisplayedBounds(mTmpRect2);
- setBounds(tempInsetBounds);
- }
- }
-
- /**
- * Gets the current overridden displayed bounds. These will be empty if the task is not
- * currently overriding where it is displayed.
- */
- @Override
- public Rect getDisplayedBounds() {
- if (mOverrideDisplayedBounds.isEmpty()) {
- return super.getDisplayedBounds();
- } else {
- return mOverrideDisplayedBounds;
- }
- }
-
@Override
void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets,
Rect outSurfaceInsets) {
@@ -3431,7 +3360,6 @@
pw.println(prefix + "taskId=" + mTaskId);
pw.println(doublePrefix + "mBounds=" + getBounds().toShortString());
pw.println(doublePrefix + "appTokens=" + mChildren);
- pw.println(doublePrefix + "mDisplayedBounds=" + mOverrideDisplayedBounds.toShortString());
final String triplePrefix = doublePrefix + " ";
final String quadruplePrefix = triplePrefix + " ";
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 77530fb..899ab24 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2062,7 +2062,7 @@
// TODO: Remove this and use #getBounds() instead once we set an app transition animation
// on TaskStack.
Rect getAnimationBounds(int appStackClipMode) {
- return getDisplayedBounds();
+ return getBounds();
}
/**
@@ -2124,7 +2124,7 @@
// Separate position and size for use in animators.
mTmpRect.set(getAnimationBounds(appStackClipMode));
if (sHierarchicalAnimations) {
- getRelativeDisplayedPosition(mTmpPoint);
+ getRelativePosition(mTmpPoint);
} else {
mTmpPoint.set(mTmpRect.left, mTmpRect.top);
}
@@ -2399,7 +2399,7 @@
return;
}
- getRelativeDisplayedPosition(mTmpPos);
+ getRelativePosition(mTmpPos);
if (mTmpPos.equals(mLastSurfacePosition)) {
return;
}
@@ -2414,16 +2414,6 @@
}
/**
- * Displayed bounds specify where to display this container at. It differs from bounds during
- * certain operations (like animation or interactive dragging).
- *
- * @return the bounds to display this container at.
- */
- Rect getDisplayedBounds() {
- return getBounds();
- }
-
- /**
* The {@code outFrame} retrieved by this method specifies where the animation will finish
* the entrance animation, as the next frame will display the window at these coordinates. In
* case of exit animation, this is where the animation will start, as the frame before the
@@ -2443,7 +2433,7 @@
outSurfaceInsets.setEmpty();
}
- void getRelativeDisplayedPosition(Point outPos) {
+ void getRelativePosition(Point outPos) {
// In addition to updateSurfacePosition, we keep other code that sets
// position from fighting with the organizer
if (isOrganized()) {
@@ -2451,11 +2441,11 @@
return;
}
- final Rect dispBounds = getDisplayedBounds();
+ final Rect dispBounds = getBounds();
outPos.set(dispBounds.left, dispBounds.top);
final WindowContainer parent = getParent();
if (parent != null) {
- final Rect parentBounds = parent.getDisplayedBounds();
+ final Rect parentBounds = parent.getBounds();
outPos.offset(-parentBounds.left, -parentBounds.top);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 84cc19d..f55a1b3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2797,18 +2797,6 @@
return mRecentsAnimationController;
}
- /**
- * @return Whether the next recents animation can continue to start. Called from
- * {@link RecentsAnimation#startRecentsActivity}.
- */
- boolean canStartRecentsAnimation() {
- // TODO(multi-display): currently only default display support recent activity
- if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) {
- return false;
- }
- return true;
- }
-
void cancelRecentsAnimation(
@RecentsAnimationController.ReorderMode int reorderMode, String reason) {
if (mRecentsAnimationController != null) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index d9c0219..8b27667 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -320,7 +320,7 @@
final ActivityStack stack = (ActivityStack) container;
if (stack.inPinnedWindowingMode()) {
stack.resize(config.windowConfiguration.getBounds(),
- null /* configBounds */, PRESERVE_WINDOWS, true /* deferResume */);
+ PRESERVE_WINDOWS, true /* deferResume */);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6fe10bf..00c84ec 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -999,18 +999,6 @@
frame.inset(left, top, right, bottom);
}
- @Override
- public Rect getDisplayedBounds() {
- final Task task = getTask();
- if (task != null) {
- Rect bounds = task.getOverrideDisplayedBounds();
- if (!bounds.isEmpty()) {
- return bounds;
- }
- }
- return super.getDisplayedBounds();
- }
-
void computeFrame(DisplayFrames displayFrames) {
getLayoutingWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
computeFrameLw();
@@ -1065,7 +1053,7 @@
layoutXDiff = 0;
layoutYDiff = 0;
} else {
- windowFrames.mContainingFrame.set(getDisplayedBounds());
+ windowFrames.mContainingFrame.set(getBounds());
if (mActivityRecord != null && !mActivityRecord.mFrozenBounds.isEmpty()) {
// If the bounds are frozen, we still want to translate the window freely and only
@@ -1223,7 +1211,7 @@
parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left;
parentTop = ((WindowState) parent).mWindowFrames.mFrame.top;
} else if (parent != null) {
- final Rect parentBounds = parent.getDisplayedBounds();
+ final Rect parentBounds = parent.getBounds();
parentLeft = parentBounds.left;
parentTop = parentBounds.top;
}
@@ -5290,7 +5278,7 @@
outPoint.offset(-parent.mWindowFrames.mFrame.left + mTmpPoint.x,
-parent.mWindowFrames.mFrame.top + mTmpPoint.y);
} else if (parentWindowContainer != null) {
- final Rect parentBounds = parentWindowContainer.getDisplayedBounds();
+ final Rect parentBounds = parentWindowContainer.getBounds();
outPoint.offset(-parentBounds.left, -parentBounds.top);
}
@@ -5342,7 +5330,9 @@
// this promotion.
final WindowState imeTarget = getDisplayContent().mInputMethodTarget;
boolean inTokenWithAndAboveImeTarget = imeTarget != null && imeTarget != this
- && imeTarget.mToken == mToken && imeTarget.compareTo(this) <= 0;
+ && imeTarget.mToken == mToken
+ && getParent() != null
+ && imeTarget.compareTo(this) <= 0;
return inTokenWithAndAboveImeTarget;
}
return false;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d4470f8..9957707 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1030,7 +1030,7 @@
mTmpPos.x = 0;
mTmpPos.y = 0;
if (stack != null) {
- stack.getRelativeDisplayedPosition(mTmpPos);
+ stack.getRelativePosition(mTmpPos);
}
xOffset = -mTmpPos.x;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 4faed65..09d1d3a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1986,29 +1986,32 @@
Sets.newSet(
UserManager.DISALLOW_CONFIG_DATE_TIME,
UserManager.DISALLOW_ADD_USER,
- UserManager.DISALLOW_BLUETOOTH,
UserManager.DISALLOW_BLUETOOTH_SHARING,
- UserManager.DISALLOW_CONFIG_BLUETOOTH,
UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
- UserManager.DISALLOW_CONFIG_LOCATION,
UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
UserManager.DISALLOW_CONFIG_TETHERING,
- UserManager.DISALLOW_CONFIG_WIFI,
- UserManager.DISALLOW_CONTENT_CAPTURE,
- UserManager.DISALLOW_CONTENT_SUGGESTIONS,
UserManager.DISALLOW_DATA_ROAMING,
- UserManager.DISALLOW_DEBUGGING_FEATURES,
UserManager.DISALLOW_SAFE_BOOT,
- UserManager.DISALLOW_SHARE_LOCATION,
UserManager.DISALLOW_SMS,
UserManager.DISALLOW_USB_FILE_TRANSFER,
UserManager.DISALLOW_AIRPLANE_MODE,
UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
- UserManager.DISALLOW_OUTGOING_CALLS,
UserManager.DISALLOW_UNMUTE_MICROPHONE
);
+ private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_LOCAL_RESTRICTIONS =
+ Sets.newSet(
+ UserManager.DISALLOW_CONFIG_BLUETOOTH,
+ UserManager.DISALLOW_CONFIG_LOCATION,
+ UserManager.DISALLOW_CONFIG_WIFI,
+ UserManager.DISALLOW_CONTENT_CAPTURE,
+ UserManager.DISALLOW_CONTENT_SUGGESTIONS,
+ UserManager.DISALLOW_DEBUGGING_FEATURES,
+ UserManager.DISALLOW_SHARE_LOCATION,
+ UserManager.DISALLOW_OUTGOING_CALLS
+ );
+
public void testSetUserRestriction_asPoOfOrgOwnedDevice() throws Exception {
final int MANAGED_PROFILE_ADMIN_UID =
UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
@@ -2021,7 +2024,10 @@
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
for (String restriction : PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS) {
- addAndRemoveUserRestrictionOnParentDpm(restriction);
+ addAndRemoveGlobalUserRestrictionOnParentDpm(restriction);
+ }
+ for (String restriction : PROFILE_OWNER_ORGANIZATION_OWNED_LOCAL_RESTRICTIONS) {
+ addAndRemoveLocalUserRestrictionOnParentDpm(restriction);
}
parentDpm.setCameraDisabled(admin1, true);
@@ -2047,7 +2053,7 @@
reset(getServices().userManagerInternal);
}
- private void addAndRemoveUserRestrictionOnParentDpm(String restriction) {
+ private void addAndRemoveGlobalUserRestrictionOnParentDpm(String restriction) {
parentDpm.addUserRestriction(admin1, restriction);
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(DpmMockContext.CALLER_USER_HANDLE),
@@ -2063,6 +2069,22 @@
);
}
+ private void addAndRemoveLocalUserRestrictionOnParentDpm(String restriction) {
+ parentDpm.addUserRestriction(admin1, restriction);
+ verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(DpmMockContext.CALLER_USER_HANDLE),
+ MockUtils.checkUserRestrictions(),
+ MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM, restriction),
+ eq(false));
+ parentDpm.clearUserRestriction(admin1, restriction);
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(),
+ dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .getParentActiveAdmin()
+ .getEffectiveRestrictions()
+ );
+ }
+
public void testNoDefaultEnabledUserRestrictions() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_USERS);
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index 62589eb..22020ad 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -54,6 +54,7 @@
assertFalse(opt.isForce());
assertFalse(opt.isDexoptIdleBackgroundJob());
assertFalse(opt.isDexoptInstallWithDexMetadata());
+ assertFalse(opt.isDexoptInstallForRestore());
}
@Test
@@ -67,7 +68,8 @@
DexoptOptions.DEXOPT_DOWNGRADE |
DexoptOptions.DEXOPT_AS_SHARED_LIBRARY |
DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB |
- DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
+ DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE |
+ DexoptOptions.DEXOPT_FOR_RESTORE;
DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags);
assertEquals(mPackageName, opt.getPackageName());
@@ -82,6 +84,7 @@
assertTrue(opt.isDexoptAsSharedLibrary());
assertTrue(opt.isDexoptIdleBackgroundJob());
assertTrue(opt.isDexoptInstallWithDexMetadata());
+ assertTrue(opt.isDexoptInstallForRestore());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index af89761..939b7a0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -35,6 +35,7 @@
import android.os.Bundle;
import android.os.FileUtils;
import android.platform.test.annotations.Presubmit;
+import android.util.SparseIntArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -542,7 +543,12 @@
@Test
public void testUsesSdk() throws Exception {
- parsePackage("install_uses_sdk.apk_r0", R.raw.install_uses_sdk_r0, x -> x);
+ ParsedPackage pkg =
+ parsePackage("install_uses_sdk.apk_r0", R.raw.install_uses_sdk_r0, x -> x);
+ SparseIntArray minExtVers = pkg.getMinExtensionVersions();
+ assertEquals(1, minExtVers.size());
+ assertEquals(0, minExtVers.get(10000, -1));
+
try {
parsePackage("install_uses_sdk.apk_r5", R.raw.install_uses_sdk_r5, x -> x);
fail("Expected parsing exception due to incompatible extension SDK version");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 7b7470c..28ff9a5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -77,6 +77,7 @@
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -96,6 +97,10 @@
UserInfo mZero = new UserInfo(0, "zero", 0);
UserInfo mTen = new UserInfo(10, "ten", 0);
+ private String mDefaultsString;
+ private String mVersionString;
+ private final Set<ComponentName> mDefaults = new ArraySet();
+ private ManagedServices mService;
private static final String SETTING = "setting";
private static final String SECONDARY_SETTING = "secondary_setting";
@@ -106,8 +111,8 @@
private ArrayMap<Integer, String> mExpectedSecondaryComponentNames;
// type : user : list of approved
- private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedPrimary = new ArrayMap<>();
- private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedSecondary = new ArrayMap<>();
+ private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedPrimary;
+ private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedSecondary;
@Before
public void setUp() throws Exception {
@@ -132,6 +137,9 @@
profileIds.add(12);
when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
+ mVersionString = "2";
+ mExpectedPrimary = new ArrayMap<>();
+ mExpectedSecondary = new ArrayMap<>();
mExpectedPrimaryPackages = new ArrayMap<>();
mExpectedPrimaryPackages.put(0, "this.is.a.package.name:another.package");
mExpectedPrimaryPackages.put(10, "this.is.another.package");
@@ -155,6 +163,8 @@
"this.is.another.package:component:package");
mExpectedSecondary.put(APPROVAL_BY_PACKAGE, mExpectedSecondaryPackages);
mExpectedSecondary.put(APPROVAL_BY_COMPONENT, mExpectedSecondaryComponentNames);
+ mService = new TestManagedServices(getContext(), mLock, mUserProfiles,
+ mIpm, APPROVAL_BY_COMPONENT);
}
@Test
@@ -1178,9 +1188,99 @@
}
}
+ @Test
+ public void loadDefaults_noVersionNoDefaults() throws Exception {
+ resetComponentsAndPackages();
+ loadXml(mService);
+ assertEquals(mService.getDefaultComponents().size(), 0);
+ }
+
+ @Test
+ public void loadDefaults_noVersionNoDefaultsOneActive() throws Exception {
+ resetComponentsAndPackages();
+ mService.addDefaultComponentOrPackage("package/class");
+ loadXml(mService);
+ assertEquals(1, mService.getDefaultComponents().size());
+ assertTrue(mService.getDefaultComponents()
+ .contains(ComponentName.unflattenFromString("package/class")));
+ }
+
+ @Test
+ public void loadDefaults_noVersionWithDefaults() throws Exception {
+ resetComponentsAndPackages();
+ mDefaults.add(new ComponentName("default", "class"));
+ loadXml(mService);
+ assertEquals(mService.getDefaultComponents(), mDefaults);
+ }
+
+ @Test
+ public void loadDefaults_versionOneWithDefaultsWithActive() throws Exception {
+ resetComponentsAndPackages();
+ mDefaults.add(new ComponentName("default", "class"));
+ mExpectedPrimaryComponentNames.put(0, "package/class");
+ mVersionString = "1";
+ loadXml(mService);
+ assertEquals(mService.getDefaultComponents(),
+ new ArraySet(Arrays.asList(new ComponentName("package", "class"))));
+ }
+
+ @Test
+ public void loadDefaults_versionTwoWithDefaultsWithActive() throws Exception {
+ resetComponentsAndPackages();
+ mDefaults.add(new ComponentName("default", "class"));
+ mDefaultsString = "default/class";
+ mExpectedPrimaryComponentNames.put(0, "package/class");
+ mVersionString = "2";
+ loadXml(mService);
+ assertEquals(1, mService.getDefaultComponents().size());
+ mDefaults.forEach(pkg -> {
+ assertTrue(mService.getDefaultComponents().contains(pkg));
+ });
+ }
+
+ @Test
+ public void loadDefaults_versionOneWithXMLDefaultsWithActive() throws Exception {
+ resetComponentsAndPackages();
+ mDefaults.add(new ComponentName("default", "class"));
+ mDefaultsString = "xml/class";
+ mExpectedPrimaryComponentNames.put(0, "package/class");
+ mVersionString = "1";
+ loadXml(mService);
+ assertEquals(mService.getDefaultComponents(),
+ new ArraySet(Arrays.asList(new ComponentName("xml", "class"))));
+ }
+
+ @Test
+ public void loadDefaults_versionTwoWithXMLDefaultsWithActive() throws Exception {
+ resetComponentsAndPackages();
+ mDefaults.add(new ComponentName("default", "class"));
+ mDefaultsString = "xml/class";
+ mExpectedPrimaryComponentNames.put(0, "package/class");
+ mVersionString = "2";
+ loadXml(mService);
+ assertEquals(mService.getDefaultComponents(),
+ new ArraySet(Arrays.asList(new ComponentName("xml", "class"))));
+ }
+
+ private void resetComponentsAndPackages() {
+ ArrayMap<Integer, ArrayMap<Integer, String>> empty = new ArrayMap(1);
+ ArrayMap<Integer, String> emptyPkgs = new ArrayMap(0);
+ empty.append(mService.mApprovalLevel, emptyPkgs);
+ mExpectedPrimary = empty;
+ mExpectedPrimaryComponentNames = emptyPkgs;
+ mExpectedPrimaryPackages = emptyPkgs;
+ mExpectedSecondary = empty;
+ mExpectedSecondaryComponentNames = emptyPkgs;
+ mExpectedSecondaryPackages = emptyPkgs;
+ }
+
private void loadXml(ManagedServices service) throws Exception {
final StringBuffer xml = new StringBuffer();
- xml.append("<" + service.getConfig().xmlTag + ">\n");
+ String xmlTag = service.getConfig().xmlTag;
+ xml.append("<" + xmlTag
+ + (mDefaultsString != null ? " defaults=\"" + mDefaultsString + "\" " : "")
+ + (mVersionString != null ? " version=\"" + mVersionString + "\" " : "")
+ + ">\n");
for (int userId : mExpectedPrimary.get(service.mApprovalLevel).keySet()) {
xml.append(getXmlEntry(
mExpectedPrimary.get(service.mApprovalLevel).get(userId), userId, true));
@@ -1197,7 +1297,7 @@
+ ManagedServices.ATT_USER_ID + "=\"98\" "
+ ManagedServices.ATT_IS_PRIMARY + "=\"false\" "
+ ManagedServices.ATT_APPROVED_LIST + "=\"98\" />\n");
- xml.append("</" + service.getConfig().xmlTag + ">");
+ xml.append("</" + xmlTag + ">");
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new BufferedInputStream(
@@ -1224,6 +1324,7 @@
private void addExpectedServices(final ManagedServices service, final List<String> packages,
int userId) {
+ ManagedServices.Config config = service.getConfig();
when(mPm.queryIntentServicesAsUser(any(), anyInt(), eq(userId))).
thenAnswer(new Answer<List<ResolveInfo>>() {
@Override
@@ -1233,7 +1334,7 @@
Intent invocationIntent = (Intent) args[0];
if (invocationIntent != null) {
if (invocationIntent.getAction().equals(
- service.getConfig().serviceInterface)
+ config.serviceInterface)
&& packages.contains(invocationIntent.getPackage())) {
List<ResolveInfo> dummyServices = new ArrayList<>();
for (int i = 1; i <= 3; i ++) {
@@ -1431,6 +1532,11 @@
}
@Override
+ protected void loadDefaultsFromConfig() {
+ mDefaultComponents.addAll(mDefaults);
+ }
+
+ @Override
protected String getRequiredPermission() {
return null;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 88186cd..ab4dc47 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -72,13 +72,13 @@
Object mLock = new Object();
+
UserInfo mZero = new UserInfo(0, "zero", 0);
UserInfo mTen = new UserInfo(10, "ten", 0);
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
-
getContext().setMockPackageManager(mPm);
getContext().addMockSystemService(Context.USER_SERVICE, mUm);
mAssistants = spy(mNm.new NotificationAssistants(getContext(), mLock, mUserProfiles, miPm));
@@ -122,7 +122,7 @@
@Test
public void testXmlUpgradeExistingApprovedComponents() throws Exception {
- String xml = "<enabled_assistants>"
+ String xml = "<enabled_assistants version=\"2\" defaults=\"b\\b\">"
+ "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />"
+ "</enabled_assistants>";
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 2d66aa5..ecdd9e5 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -786,33 +786,16 @@
userInfos.add(new UserInfo(0, "", 0));
final ArraySet<ComponentName> validAssistants = new ArraySet<>();
validAssistants.add(ComponentName.unflattenFromString(testComponent));
- final String originalComponent = DeviceConfig.getProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE
- );
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
- testComponent,
- false
- );
when(mActivityManager.isLowRamDevice()).thenReturn(false);
when(mAssistants.queryPackageForServices(isNull(), anyInt(), anyInt()))
.thenReturn(validAssistants);
- when(mAssistants.getDefaultComponents()).thenReturn(new ArraySet<>());
+ when(mAssistants.getDefaultComponents()).thenReturn(validAssistants);
when(mUm.getEnabledProfiles(anyInt())).thenReturn(userInfos);
mService.setDefaultAssistantForUser(userId);
verify(mAssistants).setPackageOrComponentEnabled(
eq(testComponent), eq(userId), eq(true), eq(true));
-
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
- originalComponent,
- false
- );
}
@Test
@@ -6299,7 +6282,7 @@
mService.loadDefaultApprovedServices(USER_SYSTEM);
- verify(mConditionProviders, times(1)).addDefaultComponentOrPackage("test");
+ verify(mConditionProviders, times(1)).loadDefaultsFromConfig();
}
// TODO: add tests for the rest of the non-empty cases
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 4532400..17dd26e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -89,13 +89,13 @@
public void testOnPictureInPictureRequested() throws RemoteException {
final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
- ClientLifecycleManager lifecycleManager = mService.getLifecycleManager();
- doNothing().when(lifecycleManager).scheduleTransaction(any());
+ final ClientLifecycleManager mockLifecycleManager = mock(ClientLifecycleManager.class);
+ doReturn(mockLifecycleManager).when(mService).getLifecycleManager();
doReturn(true).when(activity).checkEnterPictureInPictureState(anyString(), anyBoolean());
mService.requestPictureInPictureMode(activity.token);
- verify(lifecycleManager).scheduleTransaction(mClientTransactionCaptor.capture());
+ verify(mockLifecycleManager).scheduleTransaction(mClientTransactionCaptor.capture());
final ClientTransaction transaction = mClientTransactionCaptor.getValue();
// Check that only an enter pip request item callback was scheduled.
assertEquals(1, transaction.getCallbacks().size());
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 3fd81b4..daff14992 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -65,6 +65,7 @@
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
@@ -84,10 +85,13 @@
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
import android.view.Gravity;
+import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowRotationCallback;
import android.view.IDisplayWindowRotationController;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindowManager;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceControl.Transaction;
@@ -810,25 +814,19 @@
@Test
public void testComputeImeParent_app() throws Exception {
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
- final DisplayContent dc = createNewDisplay();
- dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
- assertEquals(dc.mInputMethodTarget.mActivityRecord.getSurfaceControl(),
- dc.computeImeParent());
- }
+ final DisplayContent dc = createNewDisplay();
+ dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ assertEquals(dc.mInputMethodTarget.mActivityRecord.getSurfaceControl(),
+ dc.computeImeParent());
}
@Test
public void testComputeImeParent_app_notFullscreen() throws Exception {
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
- final DisplayContent dc = createNewDisplay();
- dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "app");
- dc.mInputMethodTarget.setWindowingMode(
- WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
- }
+ final DisplayContent dc = createNewDisplay();
+ dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "app");
+ dc.mInputMethodTarget.setWindowingMode(
+ WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
}
@Test
@@ -843,12 +841,61 @@
@Test
public void testComputeImeParent_noApp() throws Exception {
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
- final DisplayContent dc = createNewDisplay();
- dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "statusBar");
- assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
- }
+ final DisplayContent dc = createNewDisplay();
+ dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
+ }
+
+ @Test
+ public void testComputeImeControlTarget() throws Exception {
+ final DisplayContent dc = createNewDisplay();
+ dc.setRemoteInsetsController(createDisplayWindowInsetsController());
+ dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ dc.mInputMethodTarget = dc.mInputMethodInputTarget;
+ assertEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget());
+ }
+
+ @Test
+ public void testComputeImeControlTarget_splitscreen() throws Exception {
+ final DisplayContent dc = createNewDisplay();
+ dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ dc.mInputMethodInputTarget.setWindowingMode(
+ WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ dc.mInputMethodTarget = dc.mInputMethodInputTarget;
+ dc.setRemoteInsetsController(createDisplayWindowInsetsController());
+ assertNotEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget());
+ }
+
+ @Test
+ public void testComputeImeControlTarget_notMatchParentBounds() throws Exception {
+ spyOn(mAppWindow.mActivityRecord);
+ doReturn(false).when(mAppWindow.mActivityRecord).matchParentBounds();
+ mDisplayContent.mInputMethodInputTarget = mAppWindow;
+ mDisplayContent.mInputMethodTarget = mDisplayContent.mInputMethodInputTarget;
+ mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
+ assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget());
+ }
+
+ private IDisplayWindowInsetsController createDisplayWindowInsetsController() {
+ return new IDisplayWindowInsetsController.Stub() {
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) throws RemoteException {
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] insetsSourceControls) throws RemoteException {
+ }
+
+ @Override
+ public void showInsets(int i, boolean b) throws RemoteException {
+ }
+
+ @Override
+ public void hideInsets(int i, boolean b) throws RemoteException {
+ }
+ };
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 61b74b0..9f28f45 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -29,9 +29,11 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.spy;
@@ -44,7 +46,6 @@
import android.view.InsetsState;
import android.view.test.InsetsModeSession;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.AfterClass;
@@ -153,22 +154,24 @@
@Test
public void testStripForDispatch_belowIme() {
+ getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+ app.mBehindIme = true;
- getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
-
- assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+ getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+ assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
}
@Test
public void testStripForDispatch_aboveIme() {
- final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+ getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ app.mBehindIme = false;
- getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
-
- assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+ getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+ assertFalse(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
}
@Test
@@ -188,11 +191,11 @@
// Adding FLAG_NOT_FOCUSABLE makes app above IME.
app.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
mDisplayContent.computeImeTarget(true);
- mDisplayContent.setLayoutNeeded();
mDisplayContent.applySurfaceChangesTransaction();
- // app won't get IME insets while above IME.
- assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+ // app won't get visible IME insets while above IME even when IME is visible.
+ getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+ assertFalse(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
// Reset invocation counter.
clearInvocations(app);
@@ -200,49 +203,49 @@
// Removing FLAG_NOT_FOCUSABLE makes app below IME.
app.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
mDisplayContent.computeImeTarget(true);
- mDisplayContent.setLayoutNeeded();
mDisplayContent.applySurfaceChangesTransaction();
// Make sure app got notified.
verify(app, atLeast(1)).notifyInsetsChanged();
- // app will get IME insets while below IME.
- assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+ // app will get visible IME insets while below IME when IME is visible.
+ getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+ assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
}
@Test
public void testStripForDispatch_childWindow_altFocusable() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
- final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+ mDisplayContent.computeImeTarget(true);
+ mDisplayContent.setLayoutNeeded();
+ mDisplayContent.applySurfaceChangesTransaction();
- // IME cannot be the IME target.
- ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
-
- getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
-
- assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME));
+ getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+ assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForDispatch(child).getSource(ITYPE_IME).isVisible());
}
@Test
public void testStripForDispatch_childWindow_splitScreen() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+ mDisplayContent.computeImeTarget(true);
+ mDisplayContent.setLayoutNeeded();
+ mDisplayContent.applySurfaceChangesTransaction();
- // IME cannot be the IME target.
- ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
-
- getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
-
- assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME));
+ getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+ assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForDispatch(child).getSource(ITYPE_IME).isVisible());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 1f6ba7a..44ca2cd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -79,7 +79,6 @@
mService.mWindowManager.setRecentsAnimationController(mRecentsAnimationController);
doNothing().when(mService.mWindowManager).initializeRecentsAnimation(
anyInt(), any(), any(), anyInt(), any(), any());
- doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
final RecentTasks recentTasks = mService.getRecentTasks();
spyOn(recentTasks);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index ec77be8..473c1c5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -145,9 +145,5 @@
Rect bounds = new Rect(10, 10, 100, 200);
task.setBounds(bounds);
assertEquals(new Point(bounds.left, bounds.top), task.getLastSurfacePosition());
-
- Rect dispBounds = new Rect(20, 30, 110, 220);
- task.setOverrideDisplayedBounds(dispBounds);
- assertEquals(new Point(dispBounds.left, dispBounds.top), task.getLastSurfacePosition());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 7be05a3..eb2aa41 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -310,27 +310,6 @@
assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top,
resolvedTaskBounds.right - contentInsetRight,
resolvedTaskBounds.bottom - contentInsetBottom));
-
- pf.set(0, 0, logicalWidth, logicalHeight);
- // If we set displayed bounds, the insets will be computed with the main task bounds
- // but the frame will be positioned according to the displayed bounds.
- final int insetLeft = logicalWidth / 5;
- final int insetTop = logicalHeight / 5;
- final int insetRight = insetLeft + (resolvedTaskBounds.right - resolvedTaskBounds.left);
- final int insetBottom = insetTop + (resolvedTaskBounds.bottom - resolvedTaskBounds.top);
- task.setOverrideDisplayedBounds(resolvedTaskBounds);
- task.setBounds(insetLeft, insetTop, insetRight, insetBottom);
- windowFrames.setFrames(pf, pf, cf, cf, pf, cf);
- w.computeFrameLw();
- assertEquals(resolvedTaskBounds, w.getFrameLw());
- assertEquals(0, w.getRelativeFrameLw().left);
- assertEquals(0, w.getRelativeFrameLw().top);
- contentInsetRight = insetRight - cfRight;
- contentInsetBottom = insetBottom - cfBottom;
- assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
- assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top,
- resolvedTaskBounds.right - contentInsetRight,
- resolvedTaskBounds.bottom - contentInsetBottom));
}
@Test
@@ -460,33 +439,6 @@
}
@Test
- @FlakyTest(bugId = 130388666)
- public void testDisplayCutout_tempDisplayedBounds() {
- // Regular fullscreen task and window
- WindowState w = createWindow();
- final Task task = w.getTask();
- task.setBounds(new Rect(0, 0, 1000, 2000));
- task.setOverrideDisplayedBounds(new Rect(0, -500, 1000, 1500));
- w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
-
- final Rect pf = new Rect(0, -500, 1000, 1500);
- // Create a display cutout of size 50x50, aligned top-center
- final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP),
- pf.width(), pf.height());
-
- final WindowFrames windowFrames = w.getWindowFrames();
- windowFrames.setFrames(pf, pf, pf, pf, pf, pf);
- windowFrames.setDisplayCutout(cutout);
- w.computeFrameLw();
-
- assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
- assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
- assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetLeft(), 0);
- assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0);
- }
-
- @Test
public void testFreeformContentInsets() {
removeGlobalMinSizeRestriction();
// fullscreen task doesn't use bounds for computeFrame
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 71b35b6..65fb2c0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -657,4 +657,16 @@
win0.mActivityRecord.getStack().setFocusable(false);
assertTrue(win0.cantReceiveTouchInput());
}
+
+ @Test
+ public void testNeedsRelativeLayeringToIme_notAttached() {
+ WindowState sameTokenWindow = createWindow(null, TYPE_BASE_APPLICATION, mAppWindow.mToken,
+ "SameTokenWindow");
+ mDisplayContent.mInputMethodTarget = mAppWindow;
+ sameTokenWindow.mActivityRecord.getStack().setWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ assertTrue(sameTokenWindow.needsRelativeLayeringToIme());
+ sameTokenWindow.removeImmediately();
+ assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
+ }
}
diff --git a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
index c35dd3b..5352be6 100644
--- a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
+++ b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
@@ -397,7 +397,7 @@
public LogcatTimestamp() throws Exception{
long currentTimeMillis = System.currentTimeMillis();
epochTime = String.format(
- "%d.%d", currentTimeMillis/1000, currentTimeMillis%1000);
+ "%d.%03d", currentTimeMillis/1000, currentTimeMillis%1000);
Log.i(TAG, "Current logcat timestamp is " + epochTime);
}
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index a116c07..242c2e9 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -80,7 +80,6 @@
private final int mMtu;
private final int mMtuV4;
private final int mMtuV6;
- private final int mVersion;
/**
* @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -126,9 +125,7 @@
? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
mPcscfAddresses = (pcscfAddresses == null)
? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
- mMtu = mtu;
- mMtuV4 = mMtuV6 = 0;
- mVersion = 0;
+ mMtu = mMtuV4 = mMtuV6 = mtu;
}
/** @hide */
@@ -136,7 +133,7 @@
@LinkStatus int linkStatus, @ProtocolType int protocolType,
@Nullable String interfaceName, @Nullable List<LinkAddress> addresses,
@Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
- @Nullable List<InetAddress> pcscfAddresses, int mtuV4, int mtuV6, int version) {
+ @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
mId = id;
@@ -151,10 +148,9 @@
? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
mPcscfAddresses = (pcscfAddresses == null)
? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
- mMtu = 0;
+ mMtu = mtu;
mMtuV4 = mtuV4;
mMtuV6 = mtuV6;
- mVersion = version;
}
/** @hide */
@@ -177,7 +173,6 @@
mMtu = source.readInt();
mMtuV4 = source.readInt();
mMtuV6 = source.readInt();
- mVersion = source.readInt();
}
/**
@@ -247,7 +242,7 @@
*/
@Deprecated
public int getMtu() {
- return mVersion < 5 ? mMtu : 0;
+ return mMtu;
}
/**
@@ -256,7 +251,7 @@
* Zero or negative values means network has either not sent a value or sent an invalid value.
*/
public int getMtuV4() {
- return mVersion < 5 ? 0 : mMtuV4;
+ return mMtuV4;
}
/**
@@ -264,7 +259,7 @@
* Zero or negative values means network has either not sent a value or sent an invalid value.
*/
public int getMtuV6() {
- return mVersion < 5 ? 0 : mMtuV6;
+ return mMtuV6;
}
@NonNull
@@ -282,10 +277,9 @@
.append(" dnses=").append(mDnsAddresses)
.append(" gateways=").append(mGatewayAddresses)
.append(" pcscf=").append(mPcscfAddresses)
- .append(" mtu=").append(mMtu)
- .append(" mtuV4=").append(mMtuV4)
- .append(" mtuV6=").append(mMtuV6)
- .append(" version=").append(mVersion)
+ .append(" mtu=").append(getMtu())
+ .append(" mtuV4=").append(getMtuV4())
+ .append(" mtuV6=").append(getMtuV6())
.append("}");
return sb.toString();
}
@@ -315,15 +309,14 @@
&& mPcscfAddresses.containsAll(other.mPcscfAddresses)
&& mMtu == other.mMtu
&& mMtuV4 == other.mMtuV4
- && mMtuV6 == other.mMtuV6
- && mVersion == other.mVersion;
+ && mMtuV6 == other.mMtuV6;
}
@Override
public int hashCode() {
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
- mMtu, mMtuV4, mMtuV6, mVersion);
+ mMtu, mMtuV4, mMtuV6);
}
@Override
@@ -346,7 +339,6 @@
dest.writeInt(mMtu);
dest.writeInt(mMtuV4);
dest.writeInt(mMtuV6);
- dest.writeInt(mVersion);
}
public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -403,8 +395,6 @@
private int mMtuV6;
- private int mVersion;
-
/**
* Default constructor for Builder.
*/
@@ -563,29 +553,14 @@
}
/**
- * Set the IRadio version for this DataCallResponse
- * @hide
- */
- public @NonNull Builder setVersion(int version) {
- mVersion = version;
- return this;
- }
-
- /**
* Build the DataCallResponse.
*
* @return the DataCallResponse object.
*/
public @NonNull DataCallResponse build() {
- if (mVersion >= 5) {
- return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
- mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
- mPcscfAddresses, mMtuV4, mMtuV6, mVersion);
- } else {
- return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
- mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
- mPcscfAddresses, mMtu);
- }
+ return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
+ mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
+ mPcscfAddresses, mMtu, mMtuV4, mMtuV6);
}
}
}
diff --git a/tests/AppLaunch/Android.bp b/tests/AppLaunch/Android.bp
index f90f26f..75db551 100644
--- a/tests/AppLaunch/Android.bp
+++ b/tests/AppLaunch/Android.bp
@@ -8,6 +8,8 @@
"android.test.base",
"android.test.runner",
],
- static_libs: ["androidx.test.rules"],
+ static_libs: [
+ "androidx.test.rules",
+ "ub-uiautomator"],
test_suites: ["device-tests"],
}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 2d2f4db..7d750b7 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -15,6 +15,8 @@
*/
package com.android.tests.applaunch;
+import static org.junit.Assert.assertNotNull;
+
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActivityManager;
@@ -29,7 +31,9 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.support.test.uiautomator.UiDevice;
import android.test.InstrumentationTestCase;
import android.test.InstrumentationTestRunner;
import android.util.Log;
@@ -46,6 +50,7 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
+import java.nio.file.Paths;
import java.time.format.DateTimeFormatter;
import java.time.ZonedDateTime;
import java.time.ZoneOffset;
@@ -67,6 +72,7 @@
* in the following format:
* -e apps <app name>^<result key>|<app name>^<result key>
*/
+@Deprecated
public class AppLaunch extends InstrumentationTestCase {
private static final int JOIN_TIMEOUT = 10000;
@@ -94,6 +100,9 @@
private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
private static final String KEY_COMPILER_FILTERS = "compiler_filters";
private static final String KEY_FORCE_STOP_APP = "force_stop_app";
+ private static final String ENABLE_SCREEN_RECORDING = "enable_screen_recording";
+ private static final int MAX_RECORDING_PARTS = 5;
+ private static final long VIDEO_TAIL_BUFFER = 500;
private static final String SIMPLEPERF_APP_CMD =
"simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
@@ -144,14 +153,17 @@
private Map<String, Intent> mNameToIntent;
private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
+ private RecordingThread mCurrentThread;
private Map<String, String> mNameToResultKey;
private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
private IActivityManager mAm;
+ private File launchSubDir = null;
private String mSimplePerfCmd = null;
private String mLaunchOrder = null;
private boolean mDropCache = false;
private int mLaunchIterations = 10;
private boolean mForceStopApp = true;
+ private boolean mEnableRecording = false;
private int mTraceLaunchCount = 0;
private String mTraceDirectoryStr = null;
private Bundle mResult = new Bundle();
@@ -166,6 +178,7 @@
private boolean mCycleCleanUp = false;
private boolean mTraceAll = false;
private boolean mIterationCycle = false;
+ private UiDevice mDevice;
enum IorapStatus {
UNDEFINED,
@@ -222,7 +235,7 @@
}
try {
- File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+ launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
throw new IOException("Unable to create the lauch file sub directory "
@@ -923,9 +936,16 @@
mLaunchIterations = Integer.parseInt(launchIterations);
}
String forceStopApp = args.getString(KEY_FORCE_STOP_APP);
+
if (forceStopApp != null) {
mForceStopApp = Boolean.parseBoolean(forceStopApp);
}
+
+ String enableRecording = args.getString(ENABLE_SCREEN_RECORDING);
+
+ if (enableRecording != null) {
+ mEnableRecording = Boolean.parseBoolean(enableRecording);
+ }
String appList = args.getString(KEY_APPS);
if (appList == null)
return;
@@ -1038,6 +1058,9 @@
private AppLaunchResult startApp(String appName, String launchReason)
throws NameNotFoundException, RemoteException {
Log.i(TAG, "Starting " + appName);
+ if(mEnableRecording) {
+ startRecording(appName, launchReason);
+ }
Intent startIntent = mNameToIntent.get(appName);
if (startIntent == null) {
@@ -1053,6 +1076,10 @@
} catch (InterruptedException e) {
// ignore
}
+
+ if(mEnableRecording) {
+ stopRecording();
+ }
return runnable.getResult();
}
@@ -1360,4 +1387,126 @@
}
}
+
+ /**
+ * Start the screen recording while launching the app.
+ *
+ * @param appName
+ * @param launchReason
+ */
+ private void startRecording(String appName, String launchReason) {
+ Log.v(TAG, "Started Recording");
+ mCurrentThread = new RecordingThread("test-screen-record",
+ String.format("%s_%s", appName, launchReason));
+ mCurrentThread.start();
+ }
+
+ /**
+ * Stop already started screen recording.
+ */
+ private void stopRecording() {
+ // Skip if not directory.
+ if (launchSubDir == null) {
+ return;
+ }
+
+ // Add some extra time to the video end.
+ SystemClock.sleep(VIDEO_TAIL_BUFFER);
+ // Ctrl + C all screen record processes.
+ mCurrentThread.cancel();
+ // Wait for the thread to completely die.
+ try {
+ mCurrentThread.join();
+ } catch (InterruptedException ex) {
+ Log.e(TAG, "Interrupted when joining the recording thread.", ex);
+ }
+ Log.v(TAG, "Stopped Recording");
+ }
+
+ /** Returns the recording's name for part {@code part} of launch description. */
+ private File getOutputFile(String description, int part) {
+ // Omit the iteration number for the first iteration.
+ final String fileName =
+ String.format(
+ "%s-video%s.mp4", description, part == 1 ? "" : part);
+ return Paths.get(launchSubDir.getAbsolutePath(), description).toFile();
+ }
+
+
+ /**
+ * Encapsulates the start and stop screen recording logic.
+ * Copied from ScreenRecordCollector.
+ */
+ private class RecordingThread extends Thread {
+ private final String mDescription;
+ private final List<File> mRecordings;
+
+ private boolean mContinue;
+
+ public RecordingThread(String name, String description) {
+ super(name);
+
+ mContinue = true;
+ mRecordings = new ArrayList<>();
+
+ assertNotNull("No test description provided for recording.", description);
+ mDescription = description;
+ }
+
+ @Override
+ public void run() {
+ try {
+ // Start at i = 1 to encode parts as X.mp4, X2.mp4, X3.mp4, etc.
+ for (int i = 1; i <= MAX_RECORDING_PARTS && mContinue; i++) {
+ File output = getOutputFile(mDescription, i);
+ Log.d(
+ TAG,
+ String.format("Recording screen to %s", output.getAbsolutePath()));
+ mRecordings.add(output);
+ // Make sure not to block on this background command in the main thread so
+ // that the test continues to run, but block in this thread so it does not
+ // trigger a new screen recording session before the prior one completes.
+ getDevice().executeShellCommand(
+ String.format("screenrecord %s", output.getAbsolutePath()));
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Caught exception while screen recording.");
+ }
+ }
+
+ public void cancel() {
+ mContinue = false;
+
+ // Identify the screenrecord PIDs and send SIGINT 2 (Ctrl + C) to each.
+ try {
+ String[] pids = getDevice().executeShellCommand(
+ "pidof screenrecord").split(" ");
+ for (String pid : pids) {
+ // Avoid empty process ids, because of weird splitting behavior.
+ if (pid.isEmpty()) {
+ continue;
+ }
+
+ getDevice().executeShellCommand(
+ String.format("kill -2 %s", pid));
+ Log.d(
+ TAG,
+ String.format("Sent SIGINT 2 to screenrecord process (%s)", pid));
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to kill screen recording process.");
+ }
+ }
+
+ public List<File> getRecordings() {
+ return mRecordings;
+ }
+ }
+
+ public UiDevice getDevice() {
+ if (mDevice == null) {
+ mDevice = UiDevice.getInstance(getInstrumentation());
+ }
+ return mDevice;
+ }
}
diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp
index 5fe9498..b339950 100644
--- a/tools/stats_log_api_gen/atoms_info_writer.cpp
+++ b/tools/stats_log_api_gen/atoms_info_writer.cpp
@@ -26,51 +26,14 @@
namespace stats_log_api_gen {
static void write_atoms_info_header_body(FILE* out, const Atoms& atoms) {
- fprintf(out, "static int UNSET_VALUE = INT_MAX;\n");
- fprintf(out, "static int FIRST_UID_IN_CHAIN = 0;\n");
-
- fprintf(out, "struct StateAtomFieldOptions {\n");
- fprintf(out, " std::vector<int> primaryFields;\n");
- fprintf(out, " int exclusiveField;\n");
- fprintf(out, " int defaultState = UNSET_VALUE;\n");
- fprintf(out, " int resetState = UNSET_VALUE;\n");
- fprintf(out, " bool nested;\n");
- fprintf(out, "};\n");
- fprintf(out, "\n");
-
fprintf(out, "struct AtomsInfo {\n");
- fprintf(out,
- " const static std::set<int> "
- "kTruncatingTimestampAtomBlackList;\n");
fprintf(out, " const static std::set<int> kAtomsWithAttributionChain;\n");
- fprintf(out,
- " const static std::map<int, StateAtomFieldOptions> "
- "kStateAtomsFieldOptions;\n");
fprintf(out, " const static std::set<int> kWhitelistedAtoms;\n");
fprintf(out, "};\n");
fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", atoms.maxPushedAtomId);
}
static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) {
- std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed",
- "audio_state_changed",
- "call_state_changed",
- "phone_signal_strength_changed",
- "mobile_bytes_transfer_by_fg_bg",
- "mobile_bytes_transfer"};
- fprintf(out,
- "const std::set<int> "
- "AtomsInfo::kTruncatingTimestampAtomBlackList = {\n");
- for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
- atomIt++) {
- if (kTruncatingAtomNames.find((*atomIt)->name) != kTruncatingAtomNames.end()) {
- const string constant = make_constant_name((*atomIt)->name);
- fprintf(out, " %d, // %s\n", (*atomIt)->code, constant.c_str());
- }
- }
-
- fprintf(out, "};\n");
- fprintf(out, "\n");
fprintf(out, "const std::set<int> AtomsInfo::kAtomsWithAttributionChain = {\n");
for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
@@ -100,49 +63,6 @@
fprintf(out, "};\n");
fprintf(out, "\n");
- fprintf(out,
- "static std::map<int, StateAtomFieldOptions> "
- "getStateAtomFieldOptions() {\n");
- fprintf(out, " std::map<int, StateAtomFieldOptions> options;\n");
- fprintf(out, " StateAtomFieldOptions* opt;\n");
- for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
- atomIt++) {
- if ((*atomIt)->primaryFields.size() == 0 && (*atomIt)->exclusiveField == 0) {
- continue;
- }
- fprintf(out,
- "\n // Adding primary and exclusive fields for atom "
- "(%d)%s\n",
- (*atomIt)->code, (*atomIt)->name.c_str());
- fprintf(out, " opt = &(options[%d /* %s */]);\n", (*atomIt)->code,
- make_constant_name((*atomIt)->name).c_str());
- fprintf(out, " opt->primaryFields.reserve(%lu);\n", (*atomIt)->primaryFields.size());
- for (const auto& field : (*atomIt)->primaryFields) {
- fprintf(out, " opt->primaryFields.push_back(%d);\n", field);
- }
-
- fprintf(out, " opt->exclusiveField = %d;\n", (*atomIt)->exclusiveField);
- if ((*atomIt)->defaultState != INT_MAX) {
- fprintf(out, " opt->defaultState = %d;\n", (*atomIt)->defaultState);
- } else {
- fprintf(out, " opt->defaultState = UNSET_VALUE;\n");
- }
-
- if ((*atomIt)->triggerStateReset != INT_MAX) {
- fprintf(out, " opt->resetState = %d;\n", (*atomIt)->triggerStateReset);
- } else {
- fprintf(out, " opt->resetState = UNSET_VALUE;\n");
- }
- fprintf(out, " opt->nested = %d;\n", (*atomIt)->nested);
- }
-
- fprintf(out, " return options;\n");
- fprintf(out, "}\n");
-
- fprintf(out,
- "const std::map<int, StateAtomFieldOptions> "
- "AtomsInfo::kStateAtomsFieldOptions = "
- "getStateAtomFieldOptions();\n");
}
int write_atoms_info_header(FILE* out, const Atoms& atoms, const string& namespaceStr) {