Merge "AOD: Add additional delayed dozeTimeTick after unpausing" into pi-dev
diff --git a/Android.mk b/Android.mk
index d7d9c90..88394d6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -196,7 +196,7 @@
-since $(SRC_API_DIR)/25.txt 25 \
-since $(SRC_API_DIR)/26.txt 26 \
-since $(SRC_API_DIR)/27.txt 27 \
- -since ./frameworks/base/api/current.txt P \
+ -since $(SRC_API_DIR)/28.txt 28 \
-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
-overview $(LOCAL_PATH)/core/java/overview.html \
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index d3aefed..d548c0a 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -64,6 +64,7 @@
const int FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS = 4;
const int FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS = 5;
const int FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS = 6;
+const int FIELD_ID_DUMP_REPORT_REASON = 8;
#define NS_PER_HOUR 3600 * NS_PER_SEC
@@ -183,7 +184,7 @@
mInReconnection = false;
StatsdStats::getInstance().noteLogLost(currentTimestampNs);
// Persist the data before we reset. Do we want this?
- WriteDataToDiskLocked();
+ WriteDataToDiskLocked(CONFIG_RESET);
// We see fresher event before we see the checkpoint. We might have lost data.
// The best we can do is to reset.
std::vector<ConfigKey> configKeys;
@@ -251,7 +252,7 @@
mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
auto it = mMetricsManagers.find(key);
if (it != mMetricsManagers.end()) {
- WriteDataToDiskLocked(it->first);
+ WriteDataToDiskLocked(it->first, CONFIG_UPDATED);
}
if (newMetricsManager->isConfigValid()) {
mUidMap->OnConfigUpdated(key);
@@ -292,6 +293,7 @@
*/
void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs,
const bool include_current_partial_bucket,
+ const DumpReportReason dumpReportReason,
vector<uint8_t>* outData) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
@@ -317,7 +319,8 @@
// Start of ConfigMetricsReport (reports).
uint64_t reportsToken =
proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS);
- onConfigMetricsReportLocked(key, dumpTimeStampNs, include_current_partial_bucket, &proto);
+ onConfigMetricsReportLocked(key, dumpTimeStampNs, include_current_partial_bucket,
+ dumpReportReason, &proto);
proto.end(reportsToken);
// End of ConfigMetricsReport (reports).
} else {
@@ -346,6 +349,7 @@
void StatsLogProcessor::onConfigMetricsReportLocked(const ConfigKey& key,
const int64_t dumpTimeStampNs,
const bool include_current_partial_bucket,
+ const DumpReportReason dumpReportReason,
ProtoOutputStream* proto) {
// We already checked whether key exists in mMetricsManagers in
// WriteDataToDisk.
@@ -374,6 +378,8 @@
(long long)lastReportWallClockNs);
proto->write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS,
(long long)getWallClockNs());
+ // Dump report reason
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_DUMP_REPORT_REASON, dumpReportReason);
}
void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs,
@@ -409,7 +415,7 @@
std::lock_guard<std::mutex> lock(mMetricsMutex);
auto it = mMetricsManagers.find(key);
if (it != mMetricsManagers.end()) {
- WriteDataToDiskLocked(key);
+ WriteDataToDiskLocked(key, CONFIG_REMOVED);
mMetricsManagers.erase(it);
mUidMap->OnConfigRemoved(key);
}
@@ -455,10 +461,11 @@
}
}
-void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key) {
+void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key,
+ const DumpReportReason dumpReportReason) {
ProtoOutputStream proto;
onConfigMetricsReportLocked(key, getElapsedRealtimeNs(),
- true /* include_current_partial_bucket*/, &proto);
+ true /* include_current_partial_bucket*/, dumpReportReason, &proto);
string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR,
(long)getWallClockSec(), key.GetUid(), (long long)key.GetId());
android::base::unique_fd fd(open(file_name.c_str(),
@@ -470,15 +477,15 @@
proto.flush(fd.get());
}
-void StatsLogProcessor::WriteDataToDiskLocked() {
+void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason) {
for (auto& pair : mMetricsManagers) {
- WriteDataToDiskLocked(pair.first);
+ WriteDataToDiskLocked(pair.first, dumpReportReason);
}
}
-void StatsLogProcessor::WriteDataToDisk() {
+void StatsLogProcessor::WriteDataToDisk(bool isShutdown) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
- WriteDataToDiskLocked();
+ WriteDataToDiskLocked(DEVICE_SHUTDOWN);
}
void StatsLogProcessor::informPullAlarmFired(const int64_t timestampNs) {
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index b91b01d..c2337c1 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -32,6 +32,17 @@
namespace os {
namespace statsd {
+// Keep this in sync with DumpReportReason enum in stats_log.proto
+enum DumpReportReason {
+ DEVICE_SHUTDOWN = 1,
+ CONFIG_UPDATED = 2,
+ CONFIG_REMOVED = 3,
+ GET_DATA_CALLED = 4,
+ ADB_DUMP = 5,
+ CONFIG_RESET = 6,
+ STATSCOMPANION_DIED = 7
+};
+
class StatsLogProcessor : public ConfigListener {
public:
StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -52,7 +63,8 @@
size_t GetMetricsSize(const ConfigKey& key) const;
void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
- const bool include_current_partial_bucket, vector<uint8_t>* outData);
+ const bool include_current_partial_bucket,
+ const DumpReportReason dumpReportReason, vector<uint8_t>* outData);
/* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */
void onAnomalyAlarmFired(
@@ -65,7 +77,7 @@
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet);
/* Flushes data to disk. Data on memory will be gone after written to disk. */
- void WriteDataToDisk();
+ void WriteDataToDisk(bool shutdown);
inline sp<UidMap> getUidMap() {
return mUidMap;
@@ -109,11 +121,12 @@
void OnConfigUpdatedLocked(
const int64_t currentTimestampNs, const ConfigKey& key, const StatsdConfig& config);
- void WriteDataToDiskLocked();
- void WriteDataToDiskLocked(const ConfigKey& key);
+ void WriteDataToDiskLocked(DumpReportReason dumpReportReason);
+ void WriteDataToDiskLocked(const ConfigKey& key, DumpReportReason dumpReportReason);
void onConfigMetricsReportLocked(const ConfigKey& key, const int64_t dumpTimeStampNs,
const bool include_current_partial_bucket,
+ const DumpReportReason dumpReportReason,
util::ProtoOutputStream* proto);
/* Check if we should send a broadcast if approaching memory limits and if we're over, we
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index d025337..5229071 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -592,7 +592,7 @@
if (good) {
vector<uint8_t> data;
mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(),
- false /* include_current_bucket*/, &data);
+ false /* include_current_bucket*/, ADB_DUMP, &data);
// TODO: print the returned StatsLogReport to file instead of printing to logcat.
if (proto) {
for (size_t i = 0; i < data.size(); i ++) {
@@ -658,7 +658,7 @@
status_t StatsService::cmd_write_data_to_disk(FILE* out) {
fprintf(out, "Writing data to disk\n");
- mProcessor->WriteDataToDisk();
+ mProcessor->WriteDataToDisk(false);
return NO_ERROR;
}
@@ -815,11 +815,10 @@
return Status::ok();
}
-Status StatsService::writeDataToDisk() {
+Status StatsService::informDeviceShutdown(bool isShutdown) {
ENFORCE_UID(AID_SYSTEM);
-
- VLOG("StatsService::writeDataToDisk");
- mProcessor->WriteDataToDisk();
+ VLOG("StatsService::informDeviceShutdown");
+ mProcessor->WriteDataToDisk(isShutdown);
return Status::ok();
}
@@ -866,8 +865,8 @@
IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
ConfigKey configKey(ipc->getCallingUid(), key);
- mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
- false /* include_current_bucket*/, output);
+ mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/,
+ GET_DATA_CALLED, output);
return Status::ok();
}
@@ -966,7 +965,7 @@
void StatsService::binderDied(const wp <IBinder>& who) {
ALOGW("statscompanion service died");
- mProcessor->WriteDataToDisk();
+ mProcessor->WriteDataToDisk(STATSCOMPANION_DIED);
mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
SubscriberReporter::getInstance().setStatsCompanionService(nullptr);
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index b5957b5..e409a71 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -66,7 +66,7 @@
const vector<String16>& app);
virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version);
virtual Status informOnePackageRemoved(const String16& app, int32_t uid);
- virtual Status writeDataToDisk();
+ virtual Status informDeviceShutdown(bool isShutdown);
/**
* Called right before we start processing events.
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index ea23623..57fe10e 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -112,10 +112,13 @@
VLOG("Unknown pull atom id %d", tagId);
return;
}
- if (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) ==
- android::util::AtomsInfo::kAtomsWithUidField.end()) {
+ int uidField;
+ auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
+ if (it == android::util::AtomsInfo::kAtomsWithUidField.end()) {
VLOG("No uid to merge for atom %d", tagId);
return;
+ } else {
+ uidField = it->second; // uidField is the field number in proto,
}
const vector<int>& additiveFields =
StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.additiveFields;
@@ -129,11 +132,13 @@
for (size_t i = 0; i < data.size(); i++) {
vector<FieldValue>* valueList = data[i]->getMutableValues();
- int err = 0;
- int uid = data[i]->GetInt(1, &err);
- if (err != 0) {
- VLOG("Bad uid field for %s", data[i]->ToString().c_str());
- return;
+ int uid;
+ if (uidField > 0 && (int)data[i]->getValues().size() >= uidField &&
+ (data[i]->getValues())[uidField - 1].mValue.getType() == INT) {
+ uid = (*data[i]->getMutableValues())[uidField - 1].mValue.int_value;
+ } else {
+ ALOGE("Malformed log, uid not found. %s", data[i]->ToString().c_str());
+ continue;
}
const int hostUid = uidMap->getHostUidOrSelf(uid);
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 8eb5327..2674171 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -221,7 +221,7 @@
{
lock_guard<mutex> lock(mMutex);
- int32_t prevVersion = 0;
+ int64_t prevVersion = 0;
auto key = std::make_pair(uid, app);
auto it = mMap.find(key);
if (it != mMap.end() && !it->second.deleted) {
@@ -324,8 +324,9 @@
(long long)record.timestampNs);
proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package);
proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_NEW_VERSION, (int)record.version);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_PREV_VERSION, (int)record.prevVersion);
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_NEW_VERSION, (long long)record.version);
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_PREV_VERSION,
+ (long long)record.prevVersion);
proto->end(changesToken);
}
}
@@ -338,8 +339,8 @@
uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
FIELD_ID_SNAPSHOT_PACKAGE_INFO);
proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
- (int)kv.second.versionCode);
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
+ (long long)kv.second.versionCode);
proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first);
proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted);
proto->end(token);
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 7222e85..755b707 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -59,11 +59,11 @@
const int64_t timestampNs;
const string package;
const int32_t uid;
- const int32_t version;
- const int32_t prevVersion;
+ const int64_t version;
+ const int64_t prevVersion;
ChangeRecord(const bool isDeletion, const int64_t timestampNs, const string& package,
- const int32_t uid, const int32_t version, const int32_t prevVersion)
+ const int32_t uid, const int64_t version, const int64_t prevVersion)
: deletion(isDeletion),
timestampNs(timestampNs),
package(package),
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index e1b6386..447e4b7 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -197,6 +197,17 @@
optional int64 current_report_wall_clock_nanos = 6;
+ enum DumpReportReason {
+ DEVICE_SHUTDOWN = 1;
+ CONFIG_UPDATED = 2;
+ CONFIG_REMOVED = 3;
+ GET_DATA_CALLED = 4;
+ ADB_DUMP = 5;
+ CONFIG_RESET = 6;
+ STATSCOMPANION_DIED = 7;
+ }
+ optional DumpReportReason dump_report_reason = 8;
+
message Annotation {
optional int64 field_int64 = 1;
optional int32 field_int32 = 2;
@@ -212,6 +223,8 @@
optional ConfigKey config_key = 1;
repeated ConfigMetricsReport reports = 2;
+
+ reserved 10;
}
message StatsdStatsReport {
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 91a40e3..004b235 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -139,7 +139,7 @@
// Expect to get no metrics, but snapshot specified above in uidmap.
vector<uint8_t> bytes;
- p.onDumpReport(key, 1, false, &bytes);
+ p.onDumpReport(key, 1, false, ADB_DUMP, &bytes);
ConfigMetricsReportList output;
output.ParseFromArray(bytes.data(), bytes.size());
@@ -167,7 +167,7 @@
// Expect to get no metrics, but snapshot specified above in uidmap.
vector<uint8_t> bytes;
- p.onDumpReport(key, 1, false, &bytes);
+ p.onDumpReport(key, 1, false, ADB_DUMP, &bytes);
ConfigMetricsReportList output;
output.ParseFromArray(bytes.data(), bytes.size());
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index 4f035be..3b24341 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -144,7 +144,8 @@
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
@@ -286,7 +287,8 @@
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
index 0758fd0..934b951 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
@@ -172,8 +172,8 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1,
- false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
+ ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -490,7 +490,7 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
- &buffer);
+ ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -733,7 +733,8 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
index e74be1e..9f20754 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
@@ -130,7 +130,8 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -342,7 +343,8 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -522,7 +524,8 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -720,7 +723,8 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
index c32048b..3f193ac 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
@@ -143,7 +143,7 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
- &buffer);
+ ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -435,7 +435,7 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
- &buffer);
+ ADB_DUMP, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -652,7 +652,8 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index 9561fcf..f4ad0ce 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -122,7 +122,8 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, &buffer);
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(1, reports.reports_size());
@@ -240,7 +241,8 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, &buffer);
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(1, reports.reports_size());
@@ -340,7 +342,8 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, &buffer);
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(1, reports.reports_size());
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index d79cb33..98372ff 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -149,7 +149,8 @@
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(1, reports.reports_size());
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 4ace382..8020787 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -200,7 +200,8 @@
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
@@ -315,7 +316,8 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index a34aaf0..d646f73 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -46,7 +46,7 @@
IPCThreadState* ipc = IPCThreadState::self();
ConfigKey configKey(ipc->getCallingUid(), kConfigKey);
processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/,
- &output);
+ ADB_DUMP, &output);
ConfigMetricsReportList reports;
reports.ParseFromArray(output.data(), output.size());
EXPECT_EQ(1, reports.reports_size());
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index 62b6fcc..a01e91f 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -117,7 +117,8 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, &buffer);
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(1, reports.reports_size());
@@ -220,7 +221,8 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, &buffer);
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(1, reports.reports_size());
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index 55bf4be..974e442 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -127,7 +127,8 @@
FeedEvents(config, processor);
vector<uint8_t> buffer;
ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -161,7 +162,8 @@
vector<uint8_t> buffer;
ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
@@ -208,7 +210,8 @@
processor->OnLogEvent(event.get());
}
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
@@ -237,7 +240,8 @@
FeedEvents(config, processor);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -262,7 +266,8 @@
FeedEvents(config, processor);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
@@ -304,7 +309,8 @@
processor->OnLogEvent(event.get());
}
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, ADB_DUMP,
+ &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
diff --git a/config/hiddenapi-blacklist.txt b/config/hiddenapi-blacklist.txt
deleted file mode 100644
index e69de29..0000000
--- a/config/hiddenapi-blacklist.txt
+++ /dev/null
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index fa5c808..0213622 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -319,6 +319,7 @@
Landroid/app/LoadedApk;->mDataDirFile:Ljava/io/File;
Landroid/app/LoadedApk;->mDataDir:Ljava/lang/String;
Landroid/app/LoadedApk;->mDisplayAdjustments:Landroid/view/DisplayAdjustments;
+Landroid/app/LoadedApk;->mLibDir:Ljava/lang/String;
Landroid/app/LoadedApk;->mPackageName:Ljava/lang/String;
Landroid/app/LoadedApk;->mReceivers:Landroid/util/ArrayMap;
Landroid/app/LoadedApk;->mResDir:Ljava/lang/String;
@@ -372,6 +373,9 @@
Landroid/app/SharedPreferencesImpl;-><init>(Ljava/io/File;I)V
Landroid/app/SharedPreferencesImpl;->mFile:Ljava/io/File;
Landroid/app/SharedPreferencesImpl;->startReloadIfChangedUnexpectedly()V
+Landroid/app/slice/SliceItem;->getTimestamp()J
+Landroid/app/slice/SliceManager;->bindSlice(Landroid/net/Uri;Ljava/util/List;)Landroid/app/slice/Slice;
+Landroid/app/slice/SliceManager;->pinSlice(Landroid/net/Uri;Ljava/util/List;)V
Landroid/app/StatusBarManager;->collapsePanels()V
Landroid/app/StatusBarManager;->disable(I)V
Landroid/app/StatusBarManager;->expandNotificationsPanel()V
@@ -406,17 +410,14 @@
Landroid/appwidget/AppWidgetManager;->bindAppWidgetId(ILandroid/content/ComponentName;)V
Landroid/appwidget/AppWidgetManager;->mService:Lcom/android/internal/appwidget/IAppWidgetService;
Landroid/appwidget/AppWidgetProviderInfo;->providerInfo:Landroid/content/pm/ActivityInfo;
+Landroid/bluetooth/BluetoothA2dp;->ACTION_ACTIVE_DEVICE_CHANGED:Ljava/lang/String;
+Landroid/bluetooth/BluetoothA2dp;->ACTION_CODEC_CONFIG_CHANGED:Ljava/lang/String;
Landroid/bluetooth/BluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z
Landroid/bluetooth/BluetoothA2dp;->disableOptionalCodecs(Landroid/bluetooth/BluetoothDevice;)V
Landroid/bluetooth/BluetoothA2dp;->enableOptionalCodecs(Landroid/bluetooth/BluetoothDevice;)V
Landroid/bluetooth/BluetoothA2dp;->getActiveDevice()Landroid/bluetooth/BluetoothDevice;
Landroid/bluetooth/BluetoothA2dp;->getCodecStatus(Landroid/bluetooth/BluetoothDevice;)Landroid/bluetooth/BluetoothCodecStatus;
Landroid/bluetooth/BluetoothA2dp;->getOptionalCodecsEnabled(Landroid/bluetooth/BluetoothDevice;)I
-Landroid/bluetooth/BluetoothA2dp;->setCodecConfigPreference(Landroid/bluetooth/BluetoothDevice;Landroid/bluetooth/BluetoothCodecConfig;)V
-Landroid/bluetooth/BluetoothA2dp;->setOptionalCodecsEnabled(Landroid/bluetooth/BluetoothDevice;I)V
-Landroid/bluetooth/BluetoothA2dp;->supportsOptionalCodecs(Landroid/bluetooth/BluetoothDevice;)I
-Landroid/bluetooth/BluetoothA2dp;->ACTION_ACTIVE_DEVICE_CHANGED:Ljava/lang/String;
-Landroid/bluetooth/BluetoothA2dp;->ACTION_CODEC_CONFIG_CHANGED:Ljava/lang/String;
Landroid/bluetooth/BluetoothA2dp;->OPTIONAL_CODECS_NOT_SUPPORTED:I
Landroid/bluetooth/BluetoothA2dp;->OPTIONAL_CODECS_PREF_DISABLED:I
Landroid/bluetooth/BluetoothA2dp;->OPTIONAL_CODECS_PREF_ENABLED:I
@@ -424,6 +425,9 @@
Landroid/bluetooth/BluetoothA2dp;->OPTIONAL_CODECS_SUPPORTED:I
Landroid/bluetooth/BluetoothA2dp;->OPTIONAL_CODECS_SUPPORT_UNKNOWN:I
Landroid/bluetooth/BluetoothA2dp;->setActiveDevice(Landroid/bluetooth/BluetoothDevice;)Z
+Landroid/bluetooth/BluetoothA2dp;->setCodecConfigPreference(Landroid/bluetooth/BluetoothDevice;Landroid/bluetooth/BluetoothCodecConfig;)V
+Landroid/bluetooth/BluetoothA2dp;->setOptionalCodecsEnabled(Landroid/bluetooth/BluetoothDevice;I)V
+Landroid/bluetooth/BluetoothA2dp;->supportsOptionalCodecs(Landroid/bluetooth/BluetoothDevice;)I
Landroid/bluetooth/BluetoothAdapter;->disable(Z)Z
Landroid/bluetooth/BluetoothAdapter;->factoryReset()Z
Landroid/bluetooth/BluetoothAdapter;->getDiscoverableTimeout()I
@@ -432,17 +436,6 @@
Landroid/bluetooth/BluetoothAdapter;->setScanMode(II)Z
Landroid/bluetooth/BluetoothAdapter;->setScanMode(I)Z
Landroid/bluetooth/BluetoothCodecConfig;
-Landroid/bluetooth/BluetoothCodecConfig;-><init>(IIIIIJJJJ)V
-Landroid/bluetooth/BluetoothCodecConfig;->getBitsPerSample()I
-Landroid/bluetooth/BluetoothCodecConfig;->getChannelMode()I
-Landroid/bluetooth/BluetoothCodecConfig;->getCodecPriority()I
-Landroid/bluetooth/BluetoothCodecConfig;->getCodecSpecific1()J
-Landroid/bluetooth/BluetoothCodecConfig;->getCodecSpecific2()J
-Landroid/bluetooth/BluetoothCodecConfig;->getCodecSpecific3()J
-Landroid/bluetooth/BluetoothCodecConfig;->getCodecSpecific4()J
-Landroid/bluetooth/BluetoothCodecConfig;->getCodecType()I
-Landroid/bluetooth/BluetoothCodecConfig;->getSampleRate()I
-Landroid/bluetooth/BluetoothCodecConfig;->setCodecPriority(I)V
Landroid/bluetooth/BluetoothCodecConfig;->BITS_PER_SAMPLE_16:I
Landroid/bluetooth/BluetoothCodecConfig;->BITS_PER_SAMPLE_24:I
Landroid/bluetooth/BluetoothCodecConfig;->BITS_PER_SAMPLE_32:I
@@ -453,6 +446,16 @@
Landroid/bluetooth/BluetoothCodecConfig;->CODEC_PRIORITY_DEFAULT:I
Landroid/bluetooth/BluetoothCodecConfig;->CODEC_PRIORITY_DISABLED:I
Landroid/bluetooth/BluetoothCodecConfig;->CODEC_PRIORITY_HIGHEST:I
+Landroid/bluetooth/BluetoothCodecConfig;->getBitsPerSample()I
+Landroid/bluetooth/BluetoothCodecConfig;->getChannelMode()I
+Landroid/bluetooth/BluetoothCodecConfig;->getCodecPriority()I
+Landroid/bluetooth/BluetoothCodecConfig;->getCodecSpecific1()J
+Landroid/bluetooth/BluetoothCodecConfig;->getCodecSpecific2()J
+Landroid/bluetooth/BluetoothCodecConfig;->getCodecSpecific3()J
+Landroid/bluetooth/BluetoothCodecConfig;->getCodecSpecific4()J
+Landroid/bluetooth/BluetoothCodecConfig;->getCodecType()I
+Landroid/bluetooth/BluetoothCodecConfig;->getSampleRate()I
+Landroid/bluetooth/BluetoothCodecConfig;-><init>(IIIIIJJJJ)V
Landroid/bluetooth/BluetoothCodecConfig;->SAMPLE_RATE_176400:I
Landroid/bluetooth/BluetoothCodecConfig;->SAMPLE_RATE_192000:I
Landroid/bluetooth/BluetoothCodecConfig;->SAMPLE_RATE_44100:I
@@ -460,18 +463,19 @@
Landroid/bluetooth/BluetoothCodecConfig;->SAMPLE_RATE_88200:I
Landroid/bluetooth/BluetoothCodecConfig;->SAMPLE_RATE_96000:I
Landroid/bluetooth/BluetoothCodecConfig;->SAMPLE_RATE_NONE:I
+Landroid/bluetooth/BluetoothCodecConfig;->setCodecPriority(I)V
Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_AAC:I
-Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_APTX:I
Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_APTX_HD:I
+Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_APTX:I
Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_INVALID:I
Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_LDAC:I
Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_MAX:I
Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_SBC:I
Landroid/bluetooth/BluetoothCodecStatus;
+Landroid/bluetooth/BluetoothCodecStatus;->EXTRA_CODEC_STATUS:Ljava/lang/String;
Landroid/bluetooth/BluetoothCodecStatus;->getCodecConfig()Landroid/bluetooth/BluetoothCodecConfig;
Landroid/bluetooth/BluetoothCodecStatus;->getCodecsLocalCapabilities()[Landroid/bluetooth/BluetoothCodecConfig;
Landroid/bluetooth/BluetoothCodecStatus;->getCodecsSelectableCapabilities()[Landroid/bluetooth/BluetoothCodecConfig;
-Landroid/bluetooth/BluetoothCodecStatus;->EXTRA_CODEC_STATUS:Ljava/lang/String;
Landroid/bluetooth/BluetoothDevice;->createBond(I)Z
Landroid/bluetooth/BluetoothDevice;->getAlias()Ljava/lang/String;
Landroid/bluetooth/BluetoothDevice;->getAliasName()Ljava/lang/String;
@@ -481,6 +485,7 @@
Landroid/bluetooth/BluetoothGattDescriptor;->mInstance:I
Landroid/bluetooth/BluetoothGatt;->mAuthRetryState:I
Landroid/bluetooth/BluetoothGatt;->refresh()Z
+Landroid/bluetooth/BluetoothHeadset;->ACTION_ACTIVE_DEVICE_CHANGED:Ljava/lang/String;
Landroid/bluetooth/BluetoothHeadset;->close()V
Landroid/bluetooth/BluetoothHeadset;->connectAudio()Z
Landroid/bluetooth/BluetoothHeadset;->disconnectAudio()Z
@@ -488,10 +493,9 @@
Landroid/bluetooth/BluetoothHeadset;->setActiveDevice(Landroid/bluetooth/BluetoothDevice;)Z
Landroid/bluetooth/BluetoothHeadset;->startScoUsingVirtualVoiceCall(Landroid/bluetooth/BluetoothDevice;)Z
Landroid/bluetooth/BluetoothHeadset;->stopScoUsingVirtualVoiceCall(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothHeadset;->ACTION_ACTIVE_DEVICE_CHANGED:Ljava/lang/String;
+Landroid/bluetooth/BluetoothHearingAid;->ACTION_ACTIVE_DEVICE_CHANGED:Ljava/lang/String;
Landroid/bluetooth/BluetoothHearingAid;->getActiveDevices()Ljava/util/List;
Landroid/bluetooth/BluetoothHearingAid;->setActiveDevice(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothHearingAid;->ACTION_ACTIVE_DEVICE_CHANGED:Ljava/lang/String;
Landroid/bluetooth/BluetoothMapClient;->sendMessage(Landroid/bluetooth/BluetoothDevice;[Landroid/net/Uri;Ljava/lang/String;Landroid/app/PendingIntent;Landroid/app/PendingIntent;)Z
Landroid/bluetooth/BluetoothPan;->close()V
Landroid/bluetooth/BluetoothPan;->connect(Landroid/bluetooth/BluetoothDevice;)Z
@@ -1583,6 +1587,8 @@
Landroid/os/ServiceManager;->sServiceManager:Landroid/os/IServiceManager;
Landroid/os/SharedMemory;->getFd()I
Landroid/os/storage/DiskInfo;->getDescription()Ljava/lang/String;
+Landroid/os/storage/DiskInfo;->isSd()Z
+Landroid/os/storage/DiskInfo;->isUsb()Z
Landroid/os/storage/IStorageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IStorageManager;
Landroid/os/storage/IStorageManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/os/storage/StorageManager;->findVolumeByUuid(Ljava/lang/String;)Landroid/os/storage/VolumeInfo;
@@ -1603,13 +1609,17 @@
Landroid/os/storage/StorageVolume;->getPath()Ljava/lang/String;
Landroid/os/storage/StorageVolume;->getUserLabel()Ljava/lang/String;
Landroid/os/storage/StorageVolume;->mPath:Ljava/io/File;
+Landroid/os/storage/VolumeInfo;->buildStorageVolume(Landroid/content/Context;IZ)Landroid/os/storage/StorageVolume;
Landroid/os/storage/VolumeInfo;->getDisk()Landroid/os/storage/DiskInfo;
+Landroid/os/storage/VolumeInfo;->getEnvironmentForState(I)Ljava/lang/String;
Landroid/os/storage/VolumeInfo;->getFsUuid()Ljava/lang/String;
Landroid/os/storage/VolumeInfo;->getPath()Ljava/io/File;
Landroid/os/storage/VolumeInfo;->getState()I
Landroid/os/storage/VolumeInfo;->getType()I
Landroid/os/storage/VolumeInfo;->isPrimary()Z
Landroid/os/storage/VolumeInfo;->isVisible()Z
+Landroid/os/storage/VolumeInfo;->TYPE_EMULATED:I
+Landroid/os/storage/VolumeInfo;->TYPE_PUBLIC:I
Landroid/os/StrictMode;->conditionallyCheckInstanceCounts()V
Landroid/os/StrictMode;->disableDeathOnFileUriExposure()V
Landroid/os/StrictMode;->enterCriticalSpan(Ljava/lang/String;)Landroid/os/StrictMode$Span;
@@ -2272,6 +2282,7 @@
Landroid/view/Choreographer;->scheduleVsyncLocked()V
Landroid/view/Choreographer;->USE_VSYNC:Z
Landroid/view/ContextThemeWrapper;->getThemeResId()I
+Landroid/view/ContextThemeWrapper;->mInflater:Landroid/view/LayoutInflater;
Landroid/view/ContextThemeWrapper;->mResources:Landroid/content/res/Resources;
Landroid/view/ContextThemeWrapper;->mTheme:Landroid/content/res/Resources$Theme;
Landroid/view/ContextThemeWrapper;->mThemeResource:I
@@ -2854,8 +2865,10 @@
Landroid/widget/Switch;->mTrackDrawable:Landroid/graphics/drawable/Drawable;
Landroid/widget/TabHost$IntentContentStrategy;->getContentView()Landroid/view/View;
Landroid/widget/TabHost$IntentContentStrategy;->tabClosed()V
+Landroid/widget/TabHost$TabSpec;->mIndicatorStrategy:Landroid/widget/TabHost$IndicatorStrategy;
Landroid/widget/TabHost;->mTabSpecs:Ljava/util/List;
Landroid/widget/TabHost$TabSpec;->mContentStrategy:Landroid/widget/TabHost$ContentStrategy;
+Landroid/widget/TabWidget;->mDrawBottomStrips:Z
Landroid/widget/TabWidget;->mSelectedTab:I
Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V
Landroid/widget/TextView;->assumeLayout()V
@@ -3440,6 +3453,7 @@
Ljava/lang/reflect/Executable;->artMethod:J
Ljava/lang/reflect/Parameter;-><init>(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V
Ljava/lang/reflect/Proxy;->invoke(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/ref/Reference;->getReferent()Ljava/lang/Object;
Ljava/lang/ref/ReferenceQueue;->add(Ljava/lang/ref/Reference;)V
Ljava/lang/ref/Reference;->referent:Ljava/lang/Object;
Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V
@@ -3447,9 +3461,12 @@
Ljava/lang/Runtime;->mLibPaths:[Ljava/lang/String;
Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
Ljava/lang/Short;->value:S
+Ljava/lang/String;->getCharsNoCheck(II[CI)V
Ljava/lang/String;-><init>(II[C)V
+Ljava/lang/System;->arraycopy([CI[CII)V
Ljava/lang/System;->arraycopy([II[III)V
Ljava/lang/System;-><init>()V
+Ljava/lang/Thread;->contextClassLoader:Ljava/lang/ClassLoader;
Ljava/lang/Thread;->daemon:Z
Ljava/lang/Thread;->dispatchUncaughtException(Ljava/lang/Throwable;)V
Ljava/lang/ThreadGroup;->add(Ljava/lang/Thread;)V
@@ -3478,7 +3495,21 @@
Ljava/lang/Void;-><init>()V
Ljava/net/Authenticator;->theAuthenticator:Ljava/net/Authenticator;
Ljava/net/DatagramSocket;->impl:Ljava/net/DatagramSocketImpl;
+Ljava/net/HttpCookie;->assignors:Ljava/util/Map;
+Ljava/net/HttpCookie;->comment:Ljava/lang/String;
+Ljava/net/HttpCookie;->commentURL:Ljava/lang/String;
+Ljava/net/HttpCookie;->domain:Ljava/lang/String;
+Ljava/net/HttpCookie;->header:Ljava/lang/String;
Ljava/net/HttpCookie;->httpOnly:Z
+Ljava/net/HttpCookie;->maxAge:J
+Ljava/net/HttpCookie;->name:Ljava/lang/String;
+Ljava/net/HttpCookie;->path:Ljava/lang/String;
+Ljava/net/HttpCookie;->portlist:Ljava/lang/String;
+Ljava/net/HttpCookie;->secure:Z
+Ljava/net/HttpCookie;->toDiscard:Z
+Ljava/net/HttpCookie;->tspecials:Ljava/lang/String;
+Ljava/net/HttpCookie;->value:Ljava/lang/String;
+Ljava/net/HttpCookie;->version:I
Ljava/net/HttpCookie;->whenCreated:J
Ljava/net/Inet4Address;-><init>()V
Ljava/net/Inet6Address;->holder6:Ljava/net/Inet6Address$Inet6AddressHolder;
@@ -3569,6 +3600,8 @@
Ljava/util/PriorityQueue;->size:I
Ljava/util/Random;->seedUniquifier()J
Ljava/util/regex/Matcher;->appendPos:I
+Ljava/util/UUID;->leastSigBits:J
+Ljava/util/UUID;->mostSigBits:J
Ljava/util/Vector;->elementData(I)Ljava/lang/Object;
Ljava/util/zip/Deflater;->buf:[B
Ljava/util/zip/Deflater;->finished:Z
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index db9d923..97c9fa5 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -377,6 +377,11 @@
public abstract boolean isRecentsComponentHomeActivity(int userId);
/**
+ * Cancels any currently running recents animation.
+ */
+ public abstract void cancelRecentsAnimation(boolean restoreHomeStackPosition);
+
+ /**
* Whether an UID is active or idle.
*/
public abstract boolean isUidActive(int uid);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 1084b42..0e44833 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2831,7 +2831,7 @@
synchronized (mLock) {
if (mArtManager == null) {
try {
- mArtManager = new ArtManager(mPM.getArtManager());
+ mArtManager = new ArtManager(mContext, mPM.getArtManager());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 0f2a11a..cd12710 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -87,6 +87,7 @@
boolean onlyHasDefaultChannel(String pkg, int uid);
ParceledListSlice getRecentNotifyingAppsForUser(int userId);
int getBlockedAppCount(int userId);
+ boolean areChannelsBypassingDnd();
// TODO: Remove this when callers have been migrated to the equivalent
// INotificationListener method.
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index d42fb4c..c7618fe 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1210,11 +1210,16 @@
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
- String pkg = intent.getComponent().getPackageName();
+ String pkg = intent != null && intent.getComponent() != null
+ ? intent.getComponent().getPackageName() : null;
return getFactory(pkg).instantiateActivity(cl, className, intent);
}
private AppComponentFactory getFactory(String pkg) {
+ if (pkg == null) {
+ Log.e(TAG, "No pkg specified, disabling AppComponentFactory");
+ return AppComponentFactory.DEFAULT;
+ }
if (mThread == null) {
Log.e(TAG, "Uninitialized ActivityThread, likely app-created Instrumentation,"
+ " disabling AppComponentFactory", new Throwable());
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 8ee443b..4f88a03 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -91,6 +91,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.function.Consumer;
/**
* A class that represents how a persistent notification is to be presented to
@@ -2306,6 +2307,45 @@
}
/**
+ * Note all {@link Uri} that are referenced internally, with the expectation
+ * that Uri permission grants will need to be issued to ensure the recipient
+ * of this object is able to render its contents.
+ *
+ * @hide
+ */
+ public void visitUris(@NonNull Consumer<Uri> visitor) {
+ visitor.accept(sound);
+
+ if (tickerView != null) tickerView.visitUris(visitor);
+ if (contentView != null) contentView.visitUris(visitor);
+ if (bigContentView != null) bigContentView.visitUris(visitor);
+ if (headsUpContentView != null) headsUpContentView.visitUris(visitor);
+
+ if (extras != null) {
+ visitor.accept(extras.getParcelable(EXTRA_AUDIO_CONTENTS_URI));
+ visitor.accept(extras.getParcelable(EXTRA_BACKGROUND_IMAGE_URI));
+ }
+
+ if (MessagingStyle.class.equals(getNotificationStyle()) && extras != null) {
+ final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
+ if (!ArrayUtils.isEmpty(messages)) {
+ for (MessagingStyle.Message message : MessagingStyle.Message
+ .getMessagesFromBundleArray(messages)) {
+ visitor.accept(message.getDataUri());
+ }
+ }
+
+ final Parcelable[] historic = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
+ if (!ArrayUtils.isEmpty(historic)) {
+ for (MessagingStyle.Message message : MessagingStyle.Message
+ .getMessagesFromBundleArray(historic)) {
+ visitor.accept(message.getDataUri());
+ }
+ }
+ }
+ }
+
+ /**
* Removes heavyweight parts of the Notification object for archival or for sending to
* listeners when the full contents are not necessary.
* @hide
@@ -4574,13 +4614,14 @@
bindHeaderChronometerAndTime(contentView);
bindProfileBadge(contentView);
}
- bindActivePermissions(contentView);
+ bindActivePermissions(contentView, ambient);
bindExpandButton(contentView);
mN.mUsesStandardHeader = true;
}
- private void bindActivePermissions(RemoteViews contentView) {
- int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
+ private void bindActivePermissions(RemoteViews contentView, boolean ambient) {
+ int color = ambient ? resolveAmbientColor()
+ : isColorized() ? getPrimaryTextColor() : resolveContrastColor();
contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 757fc64..93be932 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1167,6 +1167,23 @@
public final int suppressedVisualEffects;
/**
+ * @hide
+ */
+ public static final int STATE_CHANNELS_BYPASSING_DND = 1 << 0;
+
+ /**
+ * @hide
+ */
+ public static final int STATE_UNSET = -1;
+
+ /**
+ * Notification state information that is necessary to determine Do Not Disturb behavior.
+ * Bitmask of STATE_* constants.
+ * @hide
+ */
+ public final int state;
+
+ /**
* Constructs a policy for Do Not Disturb priority mode behavior.
*
* <p>
@@ -1181,7 +1198,7 @@
*/
public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders) {
this(priorityCategories, priorityCallSenders, priorityMessageSenders,
- SUPPRESSED_EFFECTS_UNSET);
+ SUPPRESSED_EFFECTS_UNSET, STATE_UNSET);
}
/**
@@ -1219,11 +1236,23 @@
this.priorityCallSenders = priorityCallSenders;
this.priorityMessageSenders = priorityMessageSenders;
this.suppressedVisualEffects = suppressedVisualEffects;
+ this.state = STATE_UNSET;
+ }
+
+ /** @hide */
+ public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders,
+ int suppressedVisualEffects, int state) {
+ this.priorityCategories = priorityCategories;
+ this.priorityCallSenders = priorityCallSenders;
+ this.priorityMessageSenders = priorityMessageSenders;
+ this.suppressedVisualEffects = suppressedVisualEffects;
+ this.state = state;
}
/** @hide */
public Policy(Parcel source) {
- this(source.readInt(), source.readInt(), source.readInt(), source.readInt());
+ this(source.readInt(), source.readInt(), source.readInt(), source.readInt(),
+ source.readInt());
}
@Override
@@ -1232,6 +1261,7 @@
dest.writeInt(priorityCallSenders);
dest.writeInt(priorityMessageSenders);
dest.writeInt(suppressedVisualEffects);
+ dest.writeInt(state);
}
@Override
@@ -1264,6 +1294,8 @@
+ ",priorityMessageSenders=" + prioritySendersToString(priorityMessageSenders)
+ ",suppressedVisualEffects="
+ suppressedEffectsToString(suppressedVisualEffects)
+ + ",areChannelsBypassingDnd=" + (((state & STATE_CHANNELS_BYPASSING_DND) != 0)
+ ? "true" : "false")
+ "]";
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 28e5938..22df6c0 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -66,7 +66,7 @@
* @hide
*/
public static final String ACTION_REQUEST_SLICE_PERMISSION =
- "android.intent.action.REQUEST_SLICE_PERMISSION";
+ "com.android.intent.action.REQUEST_SLICE_PERMISSION";
/**
* Category used to resolve intents that can be rendered as slices.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index ee667c2..1b6b5a0 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -80,8 +80,7 @@
* {@link #getBondedDevices()}; start device discovery with
* {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
* listen for incoming RFComm connection requests with {@link
- * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
- * Channels (CoC) connection requests with listenUsingL2capCoc(int)}; or start a scan for
+ * #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for
* Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
* </p>
* <p>This class is thread safe.</p>
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
index 7ae4cb7..3a05e70 100644
--- a/core/java/android/bluetooth/BluetoothCodecStatus.java
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.java
@@ -57,13 +57,35 @@
if (o instanceof BluetoothCodecStatus) {
BluetoothCodecStatus other = (BluetoothCodecStatus) o;
return (Objects.equals(other.mCodecConfig, mCodecConfig)
- && Objects.equals(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities)
- && Objects.equals(other.mCodecsSelectableCapabilities,
+ && sameCapabilities(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities)
+ && sameCapabilities(other.mCodecsSelectableCapabilities,
mCodecsSelectableCapabilities));
}
return false;
}
+ /**
+ * Checks whether two arrays of capabilities contain same capabilities.
+ * The order of the capabilities in each array is ignored.
+ *
+ * @param c1 the first array of capabilities to compare
+ * @param c2 the second array of capabilities to compare
+ * @return true if both arrays contain same capabilities
+ */
+ private static boolean sameCapabilities(BluetoothCodecConfig[] c1,
+ BluetoothCodecConfig[] c2) {
+ if (c1 == null) {
+ return (c2 == null);
+ }
+ if (c2 == null) {
+ return false;
+ }
+ if (c1.length != c2.length) {
+ return false;
+ }
+ return Arrays.asList(c1).containsAll(Arrays.asList(c2));
+ }
+
@Override
public int hashCode() {
return Objects.hash(mCodecConfig, mCodecsLocalCapabilities,
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 4ed2500..ef1b0bd 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -701,10 +701,14 @@
* <p>If the local device has already exposed services when this function
* is called, a service update notification will be sent to all clients.
*
+ * <p>The {@link BluetoothGattServerCallback#onServiceAdded} callback will indicate
+ * whether this service has been added successfully. Do not add another service
+ * before this callback.
+ *
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param service Service to be added to the list of services provided by this device.
- * @return true, if the service has been added successfully
+ * @return true, if the request to add service has been initiated
*/
public boolean addService(BluetoothGattService service) {
if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index ead6c25..fc58533 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -396,4 +396,14 @@
mPackage = pkg;
mClass = in.readString();
}
+
+ /**
+ * Interface for classes associated with a component name.
+ * @hide
+ */
+ @FunctionalInterface
+ public interface WithComponentName {
+ /** Return the associated component name. */
+ ComponentName getComponentName();
+ }
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index f608fcb..206ed71 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2289,9 +2289,8 @@
/**
* Activity Action: Started to show more details about why an application was suspended.
*
- * <p>Whenever the system detects an activity launch for a suspended app, it shows a dialog to
- * the user to inform them of the state and present them an affordance to start this activity
- * action to show more details about the reason for suspension.
+ * <p>Whenever the system detects an activity launch for a suspended app, this action can
+ * be used to show more details about the reason for suspension.
*
* <p>Apps holding {@link android.Manifest.permission#SUSPEND_APPS} must declare an activity
* handling this intent and protect it with
diff --git a/core/java/android/content/SyncStatusInfo.java b/core/java/android/content/SyncStatusInfo.java
index ded11cfd..2d521e9 100644
--- a/core/java/android/content/SyncStatusInfo.java
+++ b/core/java/android/content/SyncStatusInfo.java
@@ -28,10 +28,15 @@
public class SyncStatusInfo implements Parcelable {
private static final String TAG = "Sync";
- static final int VERSION = 5;
+ static final int VERSION = 6;
private static final int MAX_EVENT_COUNT = 10;
+ /**
+ * Number of sync sources. KEEP THIS AND SyncStorageEngine.SOURCES IN SYNC.
+ */
+ private static final int SOURCE_COUNT = 6;
+
public final int authorityId;
/**
@@ -120,7 +125,10 @@
public long initialFailureTime;
public boolean pending;
public boolean initialize;
-
+
+ public final long[] perSourceLastSuccessTimes = new long[SOURCE_COUNT];
+ public final long[] perSourceLastFailureTimes = new long[SOURCE_COUNT];
+
// Warning: It is up to the external caller to ensure there are
// no race conditions when accessing this list
private ArrayList<Long> periodicSyncTimes;
@@ -191,6 +199,10 @@
todayStats.writeToParcel(parcel);
yesterdayStats.writeToParcel(parcel);
+
+ // Version 6.
+ parcel.writeLongArray(perSourceLastSuccessTimes);
+ parcel.writeLongArray(perSourceLastFailureTimes);
}
public SyncStatusInfo(Parcel parcel) {
@@ -260,6 +272,10 @@
todayStats.readFromParcel(parcel);
yesterdayStats.readFromParcel(parcel);
}
+ if (version >= 6) {
+ parcel.readLongArray(perSourceLastSuccessTimes);
+ parcel.readLongArray(perSourceLastFailureTimes);
+ }
}
public SyncStatusInfo(SyncStatusInfo other) {
@@ -284,6 +300,13 @@
}
mLastEventTimes.addAll(other.mLastEventTimes);
mLastEvents.addAll(other.mLastEvents);
+
+ copy(perSourceLastSuccessTimes, other.perSourceLastSuccessTimes);
+ copy(perSourceLastFailureTimes, other.perSourceLastFailureTimes);
+ }
+
+ private static void copy(long[] to, long[] from) {
+ System.arraycopy(from, 0, to, 0, to.length);
}
public void setPeriodicSyncTime(int index, long when) {
@@ -332,6 +355,34 @@
return mLastEvents.get(i);
}
+ /** Call this when a sync has succeeded. */
+ public void setLastSuccess(int source, long lastSyncTime) {
+ lastSuccessTime = lastSyncTime;
+ lastSuccessSource = source;
+ lastFailureTime = 0;
+ lastFailureSource = -1;
+ lastFailureMesg = null;
+ initialFailureTime = 0;
+
+ if (0 <= source && source < perSourceLastSuccessTimes.length) {
+ perSourceLastSuccessTimes[source] = lastSyncTime;
+ }
+ }
+
+ /** Call this when a sync has failed. */
+ public void setLastFailure(int source, long lastSyncTime, String failureMessage) {
+ lastFailureTime = lastSyncTime;
+ lastFailureSource = source;
+ lastFailureMesg = failureMessage;
+ if (initialFailureTime == 0) {
+ initialFailureTime = lastSyncTime;
+ }
+
+ if (0 <= source && source < perSourceLastFailureTimes.length) {
+ perSourceLastFailureTimes[source] = lastSyncTime;
+ }
+ }
+
public static final Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
public SyncStatusInfo createFromParcel(Parcel in) {
return new SyncStatusInfo(in);
@@ -356,7 +407,7 @@
}
/**
- * If the last reset was not not today, move today's stats to yesterday's and clear today's.
+ * If the last reset was not today, move today's stats to yesterday's and clear today's.
*/
public void maybeResetTodayStats(boolean clockValid, boolean force) {
final long now = System.currentTimeMillis();
@@ -391,4 +442,4 @@
return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)
&& c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 8223363..8717601 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -229,7 +229,7 @@
* <p>A suspending app with the permission {@code android.permission.SUSPEND_APPS} can
* optionally provide a {@link Bundle} of extra information that it deems helpful for the
* launcher to handle the suspended state of these packages. The contents of this
- * {@link Bundle} supposed to be a contract between the suspending app and the launcher.
+ * {@link Bundle} are supposed to be a contract between the suspending app and the launcher.
*
* @param packageNames The names of the packages that have just been suspended.
* @param user the user for which the given packages were suspended.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index db93e17..1d497c2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -68,6 +68,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.Locale;
/**
* Class for retrieving various kinds of information related to the application
@@ -5527,15 +5528,23 @@
*
* <p>It doesn't remove the data or the actual package file. The application's notifications
* will be hidden, any of its started activities will be stopped and it will not be able to
- * show toasts or dialogs or ring the device. When the user tries to launch a suspended app, a
- * system dialog with the given {@code dialogMessage} will be shown instead.</p>
+ * show toasts or system alert windows or ring the device.
+ *
+ * <p>When the user tries to launch a suspended app, a system dialog with the given
+ * {@code dialogMessage} will be shown instead. Since the message is supplied to the system as
+ * a {@link String}, the caller needs to take care of localization as needed.
+ * The dialog message can optionally contain a placeholder for the name of the suspended app.
+ * The system uses {@link String#format(Locale, String, Object...) String.format} to insert the
+ * app name into the message, so an example format string could be {@code "The app %1$s is
+ * currently suspended"}. This makes it easier for callers to provide a single message which
+ * works for all the packages being suspended in a single call.
*
* <p>The package must already be installed. If the package is uninstalled while suspended
* the package will no longer be suspended. </p>
*
* <p>Optionally, the suspending app can provide extra information in the form of
* {@link PersistableBundle} objects to be shared with the apps being suspended and the
- * launcher to support customization that they might need to handle the suspended state. </p>
+ * launcher to support customization that they might need to handle the suspended state.
*
* <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} or
* {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
@@ -5552,8 +5561,8 @@
* @param dialogMessage The message to be displayed to the user, when they try to launch a
* suspended app.
*
- * @return an array of package names for which the suspended status is not set as requested in
- * this method.
+ * @return an array of package names for which the suspended status could not be set as
+ * requested in this method.
*
* @hide
*/
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 0c5f228..f30b3fe 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -616,4 +616,16 @@
*/
public abstract boolean isDataRestoreSafe(@NonNull Signature restoringFromSig,
@NonNull String packageName);
+
+
+ /**
+ * Returns true if the the signing information for {@code clientUid} is sufficient to gain
+ * access gated by {@code capability}. This can happen if the two UIDs have the same signing
+ * information, if the signing information {@code clientUid} indicates that it has the signing
+ * certificate for {@code serverUid} in its signing history (if it was previously signed by it),
+ * or if the signing certificate for {@code clientUid} is in ths signing history for {@code
+ * serverUid} and with the {@code capability} specified.
+ */
+ public abstract boolean hasSignatureCapability(int serverUid, int clientUid,
+ @PackageParser.SigningDetails.CertCapabilities int capability);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 453a74a..2da2cb4 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -5706,6 +5706,9 @@
/** allow pkg to update to one signed by this certificate */
int ROLLBACK = 8;
+
+ /** allow pkg to continue to have auth access gated by this cert */
+ int AUTH = 16;
}
/**
diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java
index 4129398..b0970f4 100644
--- a/core/java/android/content/pm/dex/ArtManager.java
+++ b/core/java/android/content/pm/dex/ArtManager.java
@@ -16,12 +16,16 @@
package android.content.pm.dex;
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+import static android.Manifest.permission.READ_RUNTIME_PROFILES;
+
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.content.Context;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -62,13 +66,14 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ProfileType {}
-
- private IArtManager mArtManager;
+ private final Context mContext;
+ private final IArtManager mArtManager;
/**
* @hide
*/
- public ArtManager(@NonNull IArtManager manager) {
+ public ArtManager(@NonNull Context context, @NonNull IArtManager manager) {
+ mContext = context;
mArtManager = manager;
}
@@ -99,7 +104,7 @@
* @param callback the callback which should be used for the result
* @param executor the executor which should be used to post the result
*/
- @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+ @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS })
public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
@Nullable String codePath, @NonNull @CallbackExecutor Executor executor,
@NonNull SnapshotRuntimeProfileCallback callback) {
@@ -108,9 +113,10 @@
SnapshotRuntimeProfileCallbackDelegate delegate =
new SnapshotRuntimeProfileCallbackDelegate(callback, executor);
try {
- mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate);
+ mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate,
+ mContext.getOpPackageName());
} catch (RemoteException e) {
- e.rethrowAsRuntimeException();
+ throw e.rethrowAsRuntimeException();
}
}
@@ -122,14 +128,13 @@
* @param profileType can be either {@link ArtManager#PROFILE_APPS}
* or {@link ArtManager#PROFILE_BOOT_IMAGE}
*/
- @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+ @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS })
public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
try {
- return mArtManager.isRuntimeProfilingEnabled(profileType);
+ return mArtManager.isRuntimeProfilingEnabled(profileType, mContext.getOpPackageName());
} catch (RemoteException e) {
- e.rethrowAsRuntimeException();
+ throw e.rethrowAsRuntimeException();
}
- return false;
}
/**
diff --git a/core/java/android/content/pm/dex/IArtManager.aidl b/core/java/android/content/pm/dex/IArtManager.aidl
index 6abfdba..7f0de7e 100644
--- a/core/java/android/content/pm/dex/IArtManager.aidl
+++ b/core/java/android/content/pm/dex/IArtManager.aidl
@@ -44,8 +44,8 @@
* {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given
* {@code profileType}.
*/
- oneway void snapshotRuntimeProfile(int profileType, in String packageName,
- in String codePath, in ISnapshotRuntimeProfileCallback callback);
+ void snapshotRuntimeProfile(int profileType, in String packageName,
+ in String codePath, in ISnapshotRuntimeProfileCallback callback, String callingPackage);
/**
* Returns true if runtime profiles are enabled for the given type, false otherwise.
@@ -54,5 +54,5 @@
*
* @param profileType
*/
- boolean isRuntimeProfilingEnabled(int profileType);
+ boolean isRuntimeProfilingEnabled(int profileType, String callingPackage);
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index f525b1f..300a78b 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -50,6 +50,7 @@
private String mIfaceName;
private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>();
+ private ArrayList<InetAddress> mValidatedPrivateDnses = new ArrayList<InetAddress>();
private boolean mUsePrivateDns;
private String mPrivateDnsServerName;
private String mDomains;
@@ -167,6 +168,9 @@
mIfaceName = source.getInterfaceName();
for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
for (InetAddress i : source.getDnsServers()) mDnses.add(i);
+ for (InetAddress i : source.getValidatedPrivateDnsServers()) {
+ mValidatedPrivateDnses.add(i);
+ }
mUsePrivateDns = source.mUsePrivateDns;
mPrivateDnsServerName = source.mPrivateDnsServerName;
mDomains = source.getDomains();
@@ -374,7 +378,7 @@
* Replaces the DNS servers in this {@code LinkProperties} with
* the given {@link Collection} of {@link InetAddress} objects.
*
- * @param addresses The {@link Collection} of DNS servers to set in this object.
+ * @param dnsServers The {@link Collection} of DNS servers to set in this object.
* @hide
*/
public void setDnsServers(Collection<InetAddress> dnsServers) {
@@ -448,6 +452,65 @@
}
/**
+ * Adds the given {@link InetAddress} to the list of validated private DNS servers,
+ * if not present. This is distinct from the server name in that these are actually
+ * resolved addresses.
+ *
+ * @param dnsServer The {@link InetAddress} to add to the list of validated private DNS servers.
+ * @return true if the DNS server was added, false if it was already present.
+ * @hide
+ */
+ public boolean addValidatedPrivateDnsServer(InetAddress dnsServer) {
+ if (dnsServer != null && !mValidatedPrivateDnses.contains(dnsServer)) {
+ mValidatedPrivateDnses.add(dnsServer);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes the given {@link InetAddress} from the list of validated private DNS servers.
+ *
+ * @param dnsServer The {@link InetAddress} to remove from the list of validated private DNS
+ * servers.
+ * @return true if the DNS server was removed, false if it did not exist.
+ * @hide
+ */
+ public boolean removeValidatedPrivateDnsServer(InetAddress dnsServer) {
+ if (dnsServer != null) {
+ return mValidatedPrivateDnses.remove(dnsServer);
+ }
+ return false;
+ }
+
+ /**
+ * Replaces the validated private DNS servers in this {@code LinkProperties} with
+ * the given {@link Collection} of {@link InetAddress} objects.
+ *
+ * @param dnsServers The {@link Collection} of validated private DNS servers to set in this
+ * object.
+ * @hide
+ */
+ public void setValidatedPrivateDnsServers(Collection<InetAddress> dnsServers) {
+ mValidatedPrivateDnses.clear();
+ for (InetAddress dnsServer: dnsServers) {
+ addValidatedPrivateDnsServer(dnsServer);
+ }
+ }
+
+ /**
+ * Returns all the {@link InetAddress} for validated private DNS servers on this link.
+ * These are resolved from the private DNS server name.
+ *
+ * @return An umodifiable {@link List} of {@link InetAddress} for validated private
+ * DNS servers on this link.
+ * @hide
+ */
+ public List<InetAddress> getValidatedPrivateDnsServers() {
+ return Collections.unmodifiableList(mValidatedPrivateDnses);
+ }
+
+ /**
* Sets the DNS domain search path used on this link.
*
* @param domains A {@link String} listing in priority order the comma separated
@@ -715,6 +778,15 @@
privateDnsServerName = "PrivateDnsServerName: " + mPrivateDnsServerName + " ";
}
+ String validatedPrivateDns = "";
+ if (!mValidatedPrivateDnses.isEmpty()) {
+ validatedPrivateDns = "ValidatedPrivateDnsAddresses: [";
+ for (InetAddress addr : mValidatedPrivateDnses) {
+ validatedPrivateDns += addr.getHostAddress() + ",";
+ }
+ validatedPrivateDns += "] ";
+ }
+
String domainName = "Domains: " + mDomains;
String mtu = " MTU: " + mMtu;
@@ -959,7 +1031,7 @@
if (mDomains.equals(targetDomains) == false) return false;
}
return (mDnses.size() == targetDnses.size()) ?
- mDnses.containsAll(targetDnses) : false;
+ mDnses.containsAll(targetDnses) : false;
}
/**
@@ -977,6 +1049,20 @@
}
/**
+ * Compares this {@code LinkProperties} validated private DNS addresses against
+ * the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalValidatedPrivateDnses(LinkProperties target) {
+ Collection<InetAddress> targetDnses = target.getValidatedPrivateDnsServers();
+ return (mValidatedPrivateDnses.size() == targetDnses.size())
+ ? mValidatedPrivateDnses.containsAll(targetDnses) : false;
+ }
+
+ /**
* Compares this {@code LinkProperties} Routes against the target
*
* @param target LinkProperties to compare.
@@ -986,7 +1072,7 @@
public boolean isIdenticalRoutes(LinkProperties target) {
Collection<RouteInfo> targetRoutes = target.getRoutes();
return (mRoutes.size() == targetRoutes.size()) ?
- mRoutes.containsAll(targetRoutes) : false;
+ mRoutes.containsAll(targetRoutes) : false;
}
/**
@@ -998,7 +1084,7 @@
*/
public boolean isIdenticalHttpProxy(LinkProperties target) {
return getHttpProxy() == null ? target.getHttpProxy() == null :
- getHttpProxy().equals(target.getHttpProxy());
+ getHttpProxy().equals(target.getHttpProxy());
}
/**
@@ -1074,6 +1160,7 @@
&& isIdenticalAddresses(target)
&& isIdenticalDnses(target)
&& isIdenticalPrivateDns(target)
+ && isIdenticalValidatedPrivateDnses(target)
&& isIdenticalRoutes(target)
&& isIdenticalHttpProxy(target)
&& isIdenticalStackedLinks(target)
@@ -1121,6 +1208,19 @@
}
/**
+ * Compares the validated private DNS addresses in this LinkProperties with another
+ * LinkProperties.
+ *
+ * @param target a LinkProperties with the new list of validated private dns addresses
+ * @return the differences between the DNS addresses.
+ * @hide
+ */
+ public CompareResult<InetAddress> compareValidatedPrivateDnses(LinkProperties target) {
+ return new CompareResult<>(mValidatedPrivateDnses,
+ target != null ? target.getValidatedPrivateDnsServers() : null);
+ }
+
+ /**
* Compares all routes in this LinkProperties with another LinkProperties,
* examining both the the base link and all stacked links.
*
@@ -1168,6 +1268,7 @@
return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
+ mLinkAddresses.size() * 31
+ mDnses.size() * 37
+ + mValidatedPrivateDnses.size() * 61
+ ((null == mDomains) ? 0 : mDomains.hashCode())
+ mRoutes.size() * 41
+ ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())
@@ -1184,12 +1285,16 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(getInterfaceName());
dest.writeInt(mLinkAddresses.size());
- for(LinkAddress linkAddress : mLinkAddresses) {
+ for (LinkAddress linkAddress : mLinkAddresses) {
dest.writeParcelable(linkAddress, flags);
}
dest.writeInt(mDnses.size());
- for(InetAddress d : mDnses) {
+ for (InetAddress d : mDnses) {
+ dest.writeByteArray(d.getAddress());
+ }
+ dest.writeInt(mValidatedPrivateDnses.size());
+ for (InetAddress d : mValidatedPrivateDnses) {
dest.writeByteArray(d.getAddress());
}
dest.writeBoolean(mUsePrivateDns);
@@ -1198,7 +1303,7 @@
dest.writeInt(mMtu);
dest.writeString(mTcpBufferSizes);
dest.writeInt(mRoutes.size());
- for(RouteInfo route : mRoutes) {
+ for (RouteInfo route : mRoutes) {
dest.writeParcelable(route, flags);
}
@@ -1225,26 +1330,33 @@
netProp.setInterfaceName(iface);
}
int addressCount = in.readInt();
- for (int i=0; i<addressCount; i++) {
- netProp.addLinkAddress((LinkAddress)in.readParcelable(null));
+ for (int i = 0; i < addressCount; i++) {
+ netProp.addLinkAddress((LinkAddress) in.readParcelable(null));
}
addressCount = in.readInt();
- for (int i=0; i<addressCount; i++) {
+ for (int i = 0; i < addressCount; i++) {
try {
netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray()));
} catch (UnknownHostException e) { }
}
+ addressCount = in.readInt();
+ for (int i = 0; i < addressCount; i++) {
+ try {
+ netProp.addValidatedPrivateDnsServer(
+ InetAddress.getByAddress(in.createByteArray()));
+ } catch (UnknownHostException e) { }
+ }
netProp.setUsePrivateDns(in.readBoolean());
netProp.setPrivateDnsServerName(in.readString());
netProp.setDomains(in.readString());
netProp.setMtu(in.readInt());
netProp.setTcpBufferSizes(in.readString());
addressCount = in.readInt();
- for (int i=0; i<addressCount; i++) {
- netProp.addRoute((RouteInfo)in.readParcelable(null));
+ for (int i = 0; i < addressCount; i++) {
+ netProp.addRoute((RouteInfo) in.readParcelable(null));
}
if (in.readByte() == 1) {
- netProp.setHttpProxy((ProxyInfo)in.readParcelable(null));
+ netProp.setHttpProxy((ProxyInfo) in.readParcelable(null));
}
ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
in.readList(stackedLinks, LinkProperties.class.getClassLoader());
@@ -1259,16 +1371,16 @@
}
};
- /**
- * Check the valid MTU range based on IPv4 or IPv6.
- * @hide
- */
- public static boolean isValidMtu(int mtu, boolean ipv6) {
- if (ipv6) {
- if ((mtu >= MIN_MTU_V6 && mtu <= MAX_MTU)) return true;
- } else {
- if ((mtu >= MIN_MTU && mtu <= MAX_MTU)) return true;
- }
- return false;
+ /**
+ * Check the valid MTU range based on IPv4 or IPv6.
+ * @hide
+ */
+ public static boolean isValidMtu(int mtu, boolean ipv6) {
+ if (ipv6) {
+ if (mtu >= MIN_MTU_V6 && mtu <= MAX_MTU) return true;
+ } else {
+ if (mtu >= MIN_MTU && mtu <= MAX_MTU) return true;
}
+ return false;
+ }
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 1d232bf..0b4b921 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -4447,8 +4447,7 @@
pw.println(sb.toString());
}
- final long dischargeScreenOnCount =
- dischargeCount - dischargeScreenOffCount - dischargeScreenDozeCount;
+ final long dischargeScreenOnCount = dischargeCount - dischargeScreenOffCount;
if (dischargeScreenOnCount >= 0) {
sb.setLength(0);
sb.append(prefix);
diff --git a/core/java/android/os/ISchedulingPolicyService.aidl b/core/java/android/os/ISchedulingPolicyService.aidl
index efcf59a..78d299a 100644
--- a/core/java/android/os/ISchedulingPolicyService.aidl
+++ b/core/java/android/os/ISchedulingPolicyService.aidl
@@ -31,4 +31,13 @@
*/
int requestPriority(int pid, int tid, int prio, boolean isForApp);
+ /**
+ * Move media.codec process between SP_FOREGROUND and SP_TOP_APP.
+ * When 'enable' is 'true', server will attempt to move media.codec process
+ * from SP_FOREGROUND into SP_TOP_APP cpuset. A valid 'client' must be
+ * provided for the server to receive death notifications. When 'enable'
+ * is 'false', server will attempt to move media.codec process back to
+ * the original cpuset, and 'client' is ignored in this case.
+ */
+ int requestCpusetBoost(boolean enable, IBinder client);
}
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 6b3b93a..36c5deb 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -54,9 +54,9 @@
void informAlarmForSubscriberTriggeringFired();
/**
- * Tells statsd to store data to disk.
+ * Tells statsd that the device is about to shutdown.
*/
- void writeDataToDisk();
+ void informDeviceShutdown(boolean isShutdown);
/**
* Inform statsd what the version and package are for each uid. Note that each array should
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 21c1263..1d4d4ce 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -416,6 +416,13 @@
**/
public static final int THREAD_GROUP_RT_APP = 6;
+ /**
+ * Thread group for bound foreground services that should
+ * have additional CPU restrictions during screen off
+ * @hide
+ **/
+ public static final int THREAD_GROUP_RESTRICTED = 7;
+
public static final int SIGNAL_QUIT = 3;
public static final int SIGNAL_KILL = 9;
public static final int SIGNAL_USR1 = 10;
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 59380fd..3eaecf9 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -811,6 +811,10 @@
/**
* Detect reflective usage of APIs that are not part of the public Android SDK.
+ *
+ * <p>Note that any non-SDK APIs that this processes accesses before this detection is
+ * enabled may not be detected. To ensure that all such API accesses are detected,
+ * you should apply this policy as early as possible after process creation.
*/
public Builder detectNonSdkApiUsage() {
return enable(DETECT_VM_NON_SDK_API_USAGE);
@@ -1885,8 +1889,10 @@
if ((sVmPolicy.mask & DETECT_VM_NON_SDK_API_USAGE) != 0) {
VMRuntime.setNonSdkApiUsageConsumer(sNonSdkApiUsageConsumer);
+ VMRuntime.setDedupeHiddenApiWarnings(false);
} else {
VMRuntime.setNonSdkApiUsageConsumer(null);
+ VMRuntime.setDedupeHiddenApiWarnings(true);
}
}
}
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 673da50..6994033 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -475,11 +475,14 @@
* @param exemptions List of hidden API exemption prefixes. Any matching members are treated as
* whitelisted/public APIs (i.e. allowed, no logging of usage).
*/
- public void setApiBlacklistExemptions(List<String> exemptions) {
+ public boolean setApiBlacklistExemptions(List<String> exemptions) {
synchronized (mLock) {
mApiBlacklistExemptions = exemptions;
- maybeSetApiBlacklistExemptions(primaryZygoteState, true);
- maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
+ boolean ok = maybeSetApiBlacklistExemptions(primaryZygoteState, true);
+ if (ok) {
+ ok = maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
+ }
+ return ok;
}
}
@@ -499,12 +502,13 @@
}
@GuardedBy("mLock")
- private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
+ private boolean maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
if (state == null || state.isClosed()) {
- return;
+ Slog.e(LOG_TAG, "Can't set API blacklist exemptions: no zygote connection");
+ return false;
}
if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) {
- return;
+ return true;
}
try {
state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
@@ -520,8 +524,11 @@
if (status != 0) {
Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status);
}
+ return true;
} catch (IOException ioe) {
Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe);
+ mApiBlacklistExemptions = Collections.emptyList();
+ return false;
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2bb5ecb..c5fc067 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12676,6 +12676,18 @@
* @hide
*/
public static final String GNSS_SATELLITE_BLACKLIST = "gnss_satellite_blacklist";
+
+ /**
+ * Duration of updates in millisecond for GNSS location request from HAL to framework.
+ *
+ * If zero, the GNSS location request feature is disabled.
+ *
+ * The value is a non-negative long.
+ *
+ * @hide
+ */
+ public static final String GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS =
+ "gnss_hal_location_request_duration_millis";
}
/**
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 60537a4..6c18b45 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -496,9 +496,9 @@
*
* <p>Apps that use standard Android widgets support autofill out-of-the-box and need to do
* very little to improve their user experience (annotating autofillable views and providing
- * autofill hints). However, some apps do their own rendering and the rendered content may
- * contain semantic structure that needs to be surfaced to the autofill framework. The platform
- * exposes APIs to achieve this, however it could take some time until these apps implement
+ * autofill hints). However, some apps (typically browsers) do their own rendering and the rendered
+ * content may contain semantic structure that needs to be surfaced to the autofill framework. The
+ * platform exposes APIs to achieve this, however it could take some time until these apps implement
* autofill support.
*
* <p>To enable autofill for such apps the platform provides a compatibility mode in which the
@@ -521,15 +521,33 @@
* <meta-data android:name="android.autofill" android:resource="@xml/autofillservice" />
* </service></pre>
*
- * <P>In the XML file you can specify one or more packages for which to enable compatibility
+ * <p>In the XML file you can specify one or more packages for which to enable compatibility
* mode. Below is a sample meta-data declaration:
*
* <pre> <autofill-service xmlns:android="http://schemas.android.com/apk/res/android">
* <compatibility-package android:name="foo.bar.baz" android:maxLongVersionCode="1000000000"/>
* </autofill-service></pre>
*
- * <p>When using compatibility mode, the {@link SaveInfo.Builder#setFlags(int) SaveInfo flags}
- * automatically include {@link SaveInfo#FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE}.
+ * <p>Notice that compatibility mode has limitations such as:
+ * <ul>
+ * <li>No manual autofill requests. Hence, the {@link FillRequest}
+ * {@link FillRequest#getFlags() flags} never have the {@link FillRequest#FLAG_MANUAL_REQUEST} flag.
+ * <li>The value of password fields are most likely masked—for example, {@code ****} instead
+ * of {@code 1234}. Hence, you must be careful when using these values to avoid updating the user
+ * data with invalid input. For example, when you parse the {@link FillRequest} and detect a
+ * password field, you could check if its
+ * {@link android.app.assist.AssistStructure.ViewNode#getInputType()
+ * input type} has password flags and if so, don't add it to the {@link SaveInfo} object.
+ * <li>The autofill context is not always {@link AutofillManager#commit() committed} when an HTML
+ * form is submitted. Hence, you must use other mechanisms to trigger save, such as setting the
+ * {@link SaveInfo#FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} flag on {@link SaveInfo.Builder#setFlags(int)}
+ * or using {@link SaveInfo.Builder#setTriggerId(AutofillId)}.
+ * <li>Browsers often provide their own autofill management system. When both the browser and
+ * the platform render an autofill dialog at the same time, the result can be confusing to the user.
+ * Such browsers typically offer an option for users to disable autofill, so your service should
+ * also allow users to disable compatiblity mode for specific apps. That way, it is up to the user
+ * to decide which autofill mechanism—the browser's or the platform's—should be used.
+ * </ul>
*/
public abstract class AutofillService extends Service {
private static final String TAG = "AutofillService";
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index e3f4ad1..309fa4a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -95,6 +95,7 @@
private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false;
private static final boolean DEFAULT_ALLOW_SCREEN_OFF = false;
private static final boolean DEFAULT_ALLOW_SCREEN_ON = false;
+ private static final boolean DEFAULT_CHANNELS_BYPASSING_DND = false;
private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS =
Policy.getAllSuppressedVisualEffects();
@@ -118,6 +119,8 @@
private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
private static final String DISALLOW_TAG = "disallow";
private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects";
+ private static final String STATE_TAG = "state";
+ private static final String STATE_ATT_CHANNELS_BYPASSING_DND = "areChannelsBypassingDnd";
private static final String CONDITION_ATT_ID = "id";
private static final String CONDITION_ATT_SUMMARY = "summary";
@@ -154,6 +157,7 @@
public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS;
public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF;
public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON;
+ public boolean areChannelsBypassingDnd = DEFAULT_CHANNELS_BYPASSING_DND;
public int version;
public ZenRule manualRule;
@@ -187,6 +191,7 @@
allowMedia = source.readInt() == 1;
allowSystem = source.readInt() == 1;
suppressedVisualEffects = source.readInt();
+ areChannelsBypassingDnd = source.readInt() == 1;
}
@Override
@@ -220,6 +225,7 @@
dest.writeInt(allowMedia ? 1 : 0);
dest.writeInt(allowSystem ? 1 : 0);
dest.writeInt(suppressedVisualEffects);
+ dest.writeInt(areChannelsBypassingDnd ? 1 : 0);
}
@Override
@@ -239,6 +245,7 @@
.append(",allowWhenScreenOff=").append(allowWhenScreenOff)
.append(",allowWhenScreenOn=").append(allowWhenScreenOn)
.append(",suppressedVisualEffects=").append(suppressedVisualEffects)
+ .append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd)
.append(",automaticRules=").append(automaticRules)
.append(",manualRule=").append(manualRule)
.append(']').toString();
@@ -303,6 +310,11 @@
ZenRule.appendDiff(d, "automaticRule[" + rule + "]", fromRule, toRule);
}
ZenRule.appendDiff(d, "manualRule", manualRule, to.manualRule);
+
+ if (areChannelsBypassingDnd != to.areChannelsBypassingDnd) {
+ d.addLine("areChannelsBypassingDnd", areChannelsBypassingDnd,
+ to.areChannelsBypassingDnd);
+ }
return d;
}
@@ -397,7 +409,8 @@
&& other.user == user
&& Objects.equals(other.automaticRules, automaticRules)
&& Objects.equals(other.manualRule, manualRule)
- && other.suppressedVisualEffects == suppressedVisualEffects;
+ && other.suppressedVisualEffects == suppressedVisualEffects
+ && other.areChannelsBypassingDnd == areChannelsBypassingDnd;
}
@Override
@@ -406,7 +419,7 @@
allowRepeatCallers, allowMessages,
allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
allowWhenScreenOff, allowWhenScreenOn, user, automaticRules, manualRule,
- suppressedVisualEffects);
+ suppressedVisualEffects, areChannelsBypassingDnd);
}
private static String toDayList(int[] days) {
@@ -511,6 +524,9 @@
automaticRule.id = id;
rt.automaticRules.put(id, automaticRule);
}
+ } else if (STATE_TAG.equals(tag)) {
+ rt.areChannelsBypassingDnd = safeBoolean(parser,
+ STATE_ATT_CHANNELS_BYPASSING_DND, DEFAULT_CHANNELS_BYPASSING_DND);
}
}
}
@@ -561,6 +577,12 @@
writeRuleXml(automaticRule, out);
out.endTag(null, AUTOMATIC_TAG);
}
+
+ out.startTag(null, STATE_TAG);
+ out.attribute(null, STATE_ATT_CHANNELS_BYPASSING_DND,
+ Boolean.toString(areChannelsBypassingDnd));
+ out.endTag(null, STATE_TAG);
+
out.endTag(null, ZEN_TAG);
}
@@ -743,7 +765,8 @@
priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
- suppressedVisualEffects);
+ suppressedVisualEffects, areChannelsBypassingDnd
+ ? Policy.STATE_CHANNELS_BYPASSING_DND : 0);
}
/**
@@ -795,6 +818,9 @@
if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
suppressedVisualEffects = policy.suppressedVisualEffects;
}
+ if (policy.state != Policy.STATE_UNSET) {
+ areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
+ }
}
public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) {
@@ -1465,15 +1491,15 @@
& NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS) != 0;
boolean allowRepeatCallers = (policy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0;
+ boolean areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
return !allowReminders && !allowCalls && !allowMessages && !allowEvents
- && !allowRepeatCallers;
+ && !allowRepeatCallers && !areChannelsBypassingDnd;
}
/**
* Determines if DND is currently overriding the ringer
*/
public static boolean isZenOverridingRinger(int zen, ZenModeConfig zenConfig) {
- // TODO (beverlyt): check if apps can bypass dnd b/77729075
return zen == Global.ZEN_MODE_NO_INTERRUPTIONS
|| zen == Global.ZEN_MODE_ALARMS
|| (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
@@ -1485,7 +1511,8 @@
*/
public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(ZenModeConfig config) {
return !config.allowReminders && !config.allowCalls && !config.allowMessages
- && !config.allowEvents && !config.allowRepeatCallers;
+ && !config.allowEvents && !config.allowRepeatCallers
+ && !config.areChannelsBypassingDnd;
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 71b6084..6b16d42 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -20700,7 +20700,7 @@
if (canTakeFocus()) {
// We have a robust focus, so parents should no longer be wanting focus.
clearParentsWantFocus();
- } else if (!getViewRootImpl().isInLayout()) {
+ } else if (getViewRootImpl() == null || !getViewRootImpl().isInLayout()) {
// This is a weird case. Most-likely the user, rather than ViewRootImpl, called
// layout. In this case, there's no guarantee that parent layouts will be evaluated
// and thus the safest action is to clear focus here.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 730c372..19ead60 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1339,6 +1339,10 @@
for (int i = 0; i < mWindowStoppedCallbacks.size(); i++) {
mWindowStoppedCallbacks.get(i).windowStopped(stopped);
}
+
+ if (mStopped) {
+ mSurface.release();
+ }
}
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0f5c23f..37aca26 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -248,6 +248,12 @@
int TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE = 25;
/**
+ * A crashing activity is being closed.
+ * @hide
+ */
+ int TRANSIT_CRASHING_ACTIVITY_CLOSE = 26;
+
+ /**
* @hide
*/
@IntDef(prefix = { "TRANSIT_" }, value = {
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 96016b4..ad50dc0 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -277,12 +277,12 @@
*/
@Nullable
public static PendingIntent createPendingIntent(
- @NonNull final Context context, @NonNull final Intent intent) {
+ @NonNull final Context context, @NonNull final Intent intent, int requestCode) {
switch (getIntentType(intent, context)) {
case IntentType.ACTIVITY:
- return PendingIntent.getActivity(context, 0, intent, 0);
+ return PendingIntent.getActivity(context, requestCode, intent, 0);
case IntentType.SERVICE:
- return PendingIntent.getService(context, 0, intent, 0);
+ return PendingIntent.getService(context, requestCode, intent, 0);
default:
return null;
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 2213355..910fcaa 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -412,7 +412,7 @@
boolean isPrimaryAction = true;
for (LabeledIntent labeledIntent : IntentFactory.create(
mContext, referenceTime, highestScoringResult, classifiedText)) {
- RemoteAction action = labeledIntent.asRemoteAction(mContext);
+ final RemoteAction action = labeledIntent.asRemoteAction(mContext);
if (isPrimaryAction) {
// For O backwards compatibility, the first RemoteAction is also written to the
// legacy API fields.
@@ -421,7 +421,7 @@
builder.setIntent(labeledIntent.getIntent());
builder.setOnClickListener(TextClassification.createIntentOnClickListener(
TextClassification.createPendingIntent(mContext,
- labeledIntent.getIntent())));
+ labeledIntent.getIntent(), labeledIntent.getRequestCode())));
isPrimaryAction = false;
}
builder.addAction(action);
@@ -559,14 +559,30 @@
* Helper class to store the information from which RemoteActions are built.
*/
private static final class LabeledIntent {
- private String mTitle;
- private String mDescription;
- private Intent mIntent;
- LabeledIntent(String title, String description, Intent intent) {
+ static final int DEFAULT_REQUEST_CODE = 0;
+
+ private final String mTitle;
+ private final String mDescription;
+ private final Intent mIntent;
+ private final int mRequestCode;
+
+ /**
+ * Initializes a LabeledIntent.
+ *
+ * <p>NOTE: {@code reqestCode} is required to not be {@link #DEFAULT_REQUEST_CODE}
+ * if distinguishing info (e.g. the classified text) is represented in intent extras only.
+ * In such circumstances, the request code should represent the distinguishing info
+ * (e.g. by generating a hashcode) so that the generated PendingIntent is (somewhat)
+ * unique. To be correct, the PendingIntent should be definitely unique but we try a
+ * best effort approach that avoids spamming the system with PendingIntents.
+ */
+ // TODO: Fix the issue mentioned above so the behaviour is correct.
+ LabeledIntent(String title, String description, Intent intent, int requestCode) {
mTitle = title;
mDescription = description;
mIntent = intent;
+ mRequestCode = requestCode;
}
String getTitle() {
@@ -581,6 +597,10 @@
return mIntent;
}
+ int getRequestCode() {
+ return mRequestCode;
+ }
+
RemoteAction asRemoteAction(Context context) {
final PackageManager pm = context.getPackageManager();
final ResolveInfo resolveInfo = pm.resolveActivity(mIntent, 0);
@@ -602,8 +622,8 @@
icon = Icon.createWithResource("android",
com.android.internal.R.drawable.ic_more_items);
}
- RemoteAction action = new RemoteAction(icon, mTitle, mDescription,
- TextClassification.createPendingIntent(context, mIntent));
+ final RemoteAction action = new RemoteAction(icon, mTitle, mDescription,
+ TextClassification.createPendingIntent(context, mIntent, mRequestCode));
action.setShouldShowIcon(shouldShowIcon);
return action;
}
@@ -659,13 +679,15 @@
context.getString(com.android.internal.R.string.email),
context.getString(com.android.internal.R.string.email_desc),
new Intent(Intent.ACTION_SENDTO)
- .setData(Uri.parse(String.format("mailto:%s", text)))),
+ .setData(Uri.parse(String.format("mailto:%s", text))),
+ LabeledIntent.DEFAULT_REQUEST_CODE),
new LabeledIntent(
context.getString(com.android.internal.R.string.add_contact),
context.getString(com.android.internal.R.string.add_contact_desc),
new Intent(Intent.ACTION_INSERT_OR_EDIT)
.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
- .putExtra(ContactsContract.Intents.Insert.EMAIL, text)));
+ .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
+ text.hashCode()));
}
@NonNull
@@ -679,20 +701,23 @@
context.getString(com.android.internal.R.string.dial),
context.getString(com.android.internal.R.string.dial_desc),
new Intent(Intent.ACTION_DIAL).setData(
- Uri.parse(String.format("tel:%s", text)))));
+ Uri.parse(String.format("tel:%s", text))),
+ LabeledIntent.DEFAULT_REQUEST_CODE));
}
actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.add_contact),
context.getString(com.android.internal.R.string.add_contact_desc),
new Intent(Intent.ACTION_INSERT_OR_EDIT)
.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
- .putExtra(ContactsContract.Intents.Insert.PHONE, text)));
+ .putExtra(ContactsContract.Intents.Insert.PHONE, text),
+ text.hashCode()));
if (!userRestrictions.getBoolean(UserManager.DISALLOW_SMS, false)) {
actions.add(new LabeledIntent(
context.getString(com.android.internal.R.string.sms),
context.getString(com.android.internal.R.string.sms_desc),
new Intent(Intent.ACTION_SENDTO)
- .setData(Uri.parse(String.format("smsto:%s", text)))));
+ .setData(Uri.parse(String.format("smsto:%s", text))),
+ LabeledIntent.DEFAULT_REQUEST_CODE));
}
return actions;
}
@@ -706,7 +731,8 @@
context.getString(com.android.internal.R.string.map),
context.getString(com.android.internal.R.string.map_desc),
new Intent(Intent.ACTION_VIEW)
- .setData(Uri.parse(String.format("geo:0,0?q=%s", encText)))));
+ .setData(Uri.parse(String.format("geo:0,0?q=%s", encText))),
+ LabeledIntent.DEFAULT_REQUEST_CODE));
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG, "Could not encode address", e);
}
@@ -728,7 +754,8 @@
context.getString(com.android.internal.R.string.browse),
context.getString(com.android.internal.R.string.browse_desc),
new Intent(Intent.ACTION_VIEW, Uri.parse(text))
- .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName())));
+ .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()),
+ LabeledIntent.DEFAULT_REQUEST_CODE));
}
@NonNull
@@ -754,7 +781,8 @@
context.getString(com.android.internal.R.string.view_flight),
context.getString(com.android.internal.R.string.view_flight_desc),
new Intent(Intent.ACTION_WEB_SEARCH)
- .putExtra(SearchManager.QUERY, text)));
+ .putExtra(SearchManager.QUERY, text),
+ text.hashCode()));
}
@NonNull
@@ -765,7 +793,8 @@
return new LabeledIntent(
context.getString(com.android.internal.R.string.view_calendar),
context.getString(com.android.internal.R.string.view_calendar_desc),
- new Intent(Intent.ACTION_VIEW).setData(builder.build()));
+ new Intent(Intent.ACTION_VIEW).setData(builder.build()),
+ LabeledIntent.DEFAULT_REQUEST_CODE);
}
@NonNull
@@ -781,7 +810,8 @@
.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME,
parsedTime.toEpochMilli())
.putExtra(CalendarContract.EXTRA_EVENT_END_TIME,
- parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION));
+ parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION),
+ parsedTime.hashCode());
}
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index dac100a..f6ac1cc 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4063,7 +4063,8 @@
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
mAssistClickHandlers.put(item, TextClassification.createIntentOnClickListener(
TextClassification.createPendingIntent(mTextView.getContext(),
- textClassification.getIntent())));
+ textClassification.getIntent(),
+ createAssistMenuItemPendingIntentRequestCode())));
}
final int count = textClassification.getActions().size();
for (int i = 1; i < count; i++) {
@@ -4121,7 +4122,9 @@
final Intent intent = assistMenuItem.getIntent();
if (intent != null) {
onClickListener = TextClassification.createIntentOnClickListener(
- TextClassification.createPendingIntent(mTextView.getContext(), intent));
+ TextClassification.createPendingIntent(
+ mTextView.getContext(), intent,
+ createAssistMenuItemPendingIntentRequestCode()));
}
}
if (onClickListener != null) {
@@ -4132,6 +4135,14 @@
return true;
}
+ private int createAssistMenuItemPendingIntentRequestCode() {
+ return mTextView.hasSelection()
+ ? mTextView.getText().subSequence(
+ mTextView.getSelectionStart(), mTextView.getSelectionEnd())
+ .hashCode()
+ : 0;
+ }
+
private boolean shouldEnableAssistMenuItems() {
return mTextView.isDeviceProvisioned()
&& TextClassificationManager.getSettings(mTextView.getContext())
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index a34bd09..b6bd14e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -83,6 +83,7 @@
import java.util.Objects;
import java.util.Stack;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* A class that describes a view hierarchy that can be displayed in
@@ -444,6 +445,10 @@
return true;
}
+ public void visitUris(@NonNull Consumer<Uri> visitor) {
+ // Nothing to visit by default
+ }
+
int viewId;
}
@@ -517,6 +522,27 @@
setBitmapCache(mBitmapCache);
}
+ /**
+ * Note all {@link Uri} that are referenced internally, with the expectation
+ * that Uri permission grants will need to be issued to ensure the recipient
+ * of this object is able to render its contents.
+ *
+ * @hide
+ */
+ public void visitUris(@NonNull Consumer<Uri> visitor) {
+ if (mActions != null) {
+ for (int i = 0; i < mActions.size(); i++) {
+ mActions.get(i).visitUris(visitor);
+ }
+ }
+ }
+
+ private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
+ if (icon != null && icon.getType() == Icon.TYPE_URI) {
+ visitor.accept(icon.getUri());
+ }
+ }
+
private static class RemoteViewsContextWrapper extends ContextWrapper {
private final Context mContextForResources;
@@ -1485,6 +1511,20 @@
public boolean prefersAsyncApply() {
return this.type == URI || this.type == ICON;
}
+
+ @Override
+ public void visitUris(@NonNull Consumer<Uri> visitor) {
+ switch (this.type) {
+ case URI:
+ final Uri uri = (Uri) this.value;
+ visitor.accept(uri);
+ break;
+ case ICON:
+ final Icon icon = (Icon) this.value;
+ visitIconUri(icon, visitor);
+ break;
+ }
+ }
}
/**
@@ -1849,6 +1889,16 @@
return TEXT_VIEW_DRAWABLE_ACTION_TAG;
}
+ @Override
+ public void visitUris(@NonNull Consumer<Uri> visitor) {
+ if (useIcons) {
+ visitIconUri(i1, visitor);
+ visitIconUri(i2, visitor);
+ visitIconUri(i3, visitor);
+ visitIconUri(i4, visitor);
+ }
+ }
+
boolean isRelative = false;
boolean useIcons = false;
int d1, d2, d3, d4;
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 2b7221a..159d49b 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -54,12 +54,13 @@
void onPanelHidden();
// Mark current notifications as "seen" and stop ringing, vibrating, blinking.
void clearNotificationEffects();
- void onNotificationClick(String key);
- void onNotificationActionClick(String key, int actionIndex);
+ void onNotificationClick(String key, in NotificationVisibility nv);
+ void onNotificationActionClick(String key, int actionIndex, in NotificationVisibility nv);
void onNotificationError(String pkg, String tag, int id,
int uid, int initialPid, String message, int userId);
void onClearAllNotifications(int userId);
- void onNotificationClear(String pkg, String tag, int id, int userId, String key, int dismissalSurface);
+ void onNotificationClear(String pkg, String tag, int id, int userId, String key,
+ int dismissalSurface, in NotificationVisibility nv);
void onNotificationVisibilityChanged( in NotificationVisibility[] newlyVisibleKeys,
in NotificationVisibility[] noLongerVisibleKeys);
void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
diff --git a/core/java/com/android/internal/statusbar/NotificationVisibility.java b/core/java/com/android/internal/statusbar/NotificationVisibility.java
index 2139ad0..7fe440c 100644
--- a/core/java/com/android/internal/statusbar/NotificationVisibility.java
+++ b/core/java/com/android/internal/statusbar/NotificationVisibility.java
@@ -32,6 +32,7 @@
public String key;
public int rank;
+ public int count;
public boolean visible = true;
/*package*/ int id;
@@ -39,10 +40,11 @@
id = sNexrId++;
}
- private NotificationVisibility(String key, int rank, boolean visibile) {
+ private NotificationVisibility(String key, int rank, int count, boolean visibile) {
this();
this.key = key;
this.rank = rank;
+ this.count = count;
this.visible = visibile;
}
@@ -51,13 +53,14 @@
return "NotificationVisibility(id=" + id
+ "key=" + key
+ " rank=" + rank
+ + " count=" + count
+ (visible?" visible":"")
+ " )";
}
@Override
public NotificationVisibility clone() {
- return obtain(this.key, this.rank, this.visible);
+ return obtain(this.key, this.rank, this.count, this.visible);
}
@Override
@@ -85,12 +88,14 @@
public void writeToParcel(Parcel out, int flags) {
out.writeString(this.key);
out.writeInt(this.rank);
+ out.writeInt(this.count);
out.writeInt(this.visible ? 1 : 0);
}
private void readFromParcel(Parcel in) {
this.key = in.readString();
this.rank = in.readInt();
+ this.count = in.readInt();
this.visible = in.readInt() != 0;
}
@@ -98,10 +103,11 @@
* Return a new NotificationVisibility instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
- public static NotificationVisibility obtain(String key, int rank, boolean visible) {
+ public static NotificationVisibility obtain(String key, int rank, int count, boolean visible) {
NotificationVisibility vo = obtain();
vo.key = key;
vo.rank = rank;
+ vo.count = count;
vo.visible = visible;
return vo;
}
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 433d14f..083c0c9 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -29,6 +29,7 @@
import java.util.List;
import java.util.Set;
import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.stream.Stream;
/**
@@ -84,6 +85,17 @@
return emptyIfNull(result);
}
+ /** Add all elements matching {@code predicate} in {@code source} to {@code dest}. */
+ public static <T> void addIf(@Nullable List<T> source, @NonNull Collection<? super T> dest,
+ @Nullable Predicate<? super T> predicate) {
+ for (int i = 0; i < size(source); i++) {
+ final T item = source.get(i);
+ if (predicate.test(item)) {
+ dest.add(item);
+ }
+ }
+ }
+
/**
* Returns a list of items resulting from applying the given function to each element of the
* provided list.
diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java
index e85b782..7fd83bc 100644
--- a/core/java/com/android/internal/util/DumpUtils.java
+++ b/core/java/com/android/internal/util/DumpUtils.java
@@ -16,18 +16,25 @@
package com.android.internal.util;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Handler;
+import android.text.TextUtils;
import android.util.Slog;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.Objects;
+import java.util.function.Predicate;
/**
* Helper functions for dumping the state of system services.
+ * Test:
+ atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
*/
public final class DumpUtils {
private static final String TAG = "DumpUtils";
@@ -153,4 +160,99 @@
PrintWriter pw) {
return checkDumpPermission(context, tag, pw) && checkUsageStatsPermission(context, tag, pw);
}
+
+ /**
+ * Return whether a package name is considered to be part of the platform.
+ * @hide
+ */
+ public static boolean isPlatformPackage(@Nullable String packageName) {
+ return (packageName != null)
+ && (packageName.equals("android")
+ || packageName.startsWith("android.")
+ || packageName.startsWith("com.android."));
+ }
+
+ /**
+ * Return whether a package name is considered to be part of the platform.
+ * @hide
+ */
+ public static boolean isPlatformPackage(@Nullable ComponentName cname) {
+ return (cname != null) && isPlatformPackage(cname.getPackageName());
+ }
+
+ /**
+ * Return whether a package name is considered to be part of the platform.
+ * @hide
+ */
+ public static boolean isPlatformPackage(@Nullable ComponentName.WithComponentName wcn) {
+ return (wcn != null) && isPlatformPackage(wcn.getComponentName());
+ }
+
+ /**
+ * Return whether a package name is NOT considered to be part of the platform.
+ * @hide
+ */
+ public static boolean isNonPlatformPackage(@Nullable String packageName) {
+ return (packageName != null) && !isPlatformPackage(packageName);
+ }
+
+ /**
+ * Return whether a package name is NOT considered to be part of the platform.
+ * @hide
+ */
+ public static boolean isNonPlatformPackage(@Nullable ComponentName cname) {
+ return (cname != null) && isNonPlatformPackage(cname.getPackageName());
+ }
+
+ /**
+ * Return whether a package name is NOT considered to be part of the platform.
+ * @hide
+ */
+ public static boolean isNonPlatformPackage(@Nullable ComponentName.WithComponentName wcn) {
+ return (wcn != null) && !isPlatformPackage(wcn.getComponentName());
+ }
+
+ /**
+ * Used for dumping providers and services. Return a predicate for a given filter string.
+ * @hide
+ */
+ public static <TRec extends ComponentName.WithComponentName> Predicate<TRec> filterRecord(
+ @Nullable String filterString) {
+
+ if (TextUtils.isEmpty(filterString)) {
+ return rec -> false;
+ }
+
+ // Dump all?
+ if ("all".equals(filterString)) {
+ return Objects::nonNull;
+ }
+
+ // Dump all platform?
+ if ("all-platform".equals(filterString)) {
+ return DumpUtils::isPlatformPackage;
+ }
+
+ // Dump all non-platform?
+ if ("all-non-platform".equals(filterString)) {
+ return DumpUtils::isNonPlatformPackage;
+ }
+
+ // Is the filter a component name? If so, do an exact match.
+ final ComponentName filterCname = ComponentName.unflattenFromString(filterString);
+ if (filterCname != null) {
+ // Do exact component name check.
+ return rec -> (rec != null) && filterCname.equals(rec.getComponentName());
+ }
+
+ // Otherwise, do a partial match against the component name.
+ // Also if the filter is a hex-decimal string, do the object ID match too.
+ final int id = ParseUtils.parseIntWithBase(filterString, 16, -1);
+ return rec -> {
+ final ComponentName cn = rec.getComponentName();
+ return ((id != -1) && (System.identityHashCode(rec) == id))
+ || cn.flattenToString().toLowerCase().contains(filterString.toLowerCase());
+ };
+ }
}
+
diff --git a/core/java/com/android/internal/util/ParseUtils.java b/core/java/com/android/internal/util/ParseUtils.java
new file mode 100644
index 0000000..a591f4a
--- /dev/null
+++ b/core/java/com/android/internal/util/ParseUtils.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.util;
+
+import android.annotation.Nullable;
+
+/**
+ * Various numeric -> strings conversion.
+ *
+ * Test:
+ atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
+ */
+public final class ParseUtils {
+ private ParseUtils() {
+ }
+
+ /** Parse a value as a base-10 integer. */
+ public static int parseInt(@Nullable String value, int defValue) {
+ return parseIntWithBase(value, 10, defValue);
+ }
+
+ /** Parse a value as an integer of a given base. */
+ public static int parseIntWithBase(@Nullable String value, int base, int defValue) {
+ if (value == null) {
+ return defValue;
+ }
+ try {
+ return Integer.parseInt(value, base);
+ } catch (NumberFormatException e) {
+ return defValue;
+ }
+ }
+
+ /** Parse a value as a base-10 long. */
+ public static long parseLong(@Nullable String value, long defValue) {
+ return parseLongWithBase(value, 10, defValue);
+ }
+
+ /** Parse a value as a long of a given base. */
+ public static long parseLongWithBase(@Nullable String value, int base, long defValue) {
+ if (value == null) {
+ return defValue;
+ }
+ try {
+ return Long.parseLong(value, base);
+ } catch (NumberFormatException e) {
+ return defValue;
+ }
+ }
+
+ /** Parse a value as a float. */
+ public static float parseFloat(@Nullable String value, float defValue) {
+ if (value == null) {
+ return defValue;
+ }
+ try {
+ return Float.parseFloat(value);
+ } catch (NumberFormatException e) {
+ return defValue;
+ }
+ }
+
+ /** Parse a value as a double. */
+ public static double parseDouble(@Nullable String value, double defValue) {
+ if (value == null) {
+ return defValue;
+ }
+ try {
+ return Double.parseDouble(value);
+ } catch (NumberFormatException e) {
+ return defValue;
+ }
+ }
+
+ /** Parse a value as a boolean. */
+ public static boolean parseBoolean(@Nullable String value, boolean defValue) {
+ if ("true".equals(value)) {
+ return true;
+ }
+ if ("false".equals(value)) {
+ return false;
+ }
+ return parseInt(value, defValue ? 1 : 0) != 0;
+ }
+}
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 8be6ed8..7fa2247 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -837,7 +837,7 @@
ResTable_config selected_config;
selected_config.density = 0;
uint32_t flags = bag->type_spec_flags;
- uint32_t ref;
+ uint32_t ref = 0;
ApkAssetsCookie cookie =
assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
if (cookie == kInvalidCookie) {
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 05686a0..0765faa 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -452,6 +452,7 @@
// Secure#LOCATION_MODE_OFF} temporarily for all users.
optional SettingProto global_kill_switch = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto gnss_satellite_blacklist = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto gnss_hal_location_request_duration_millis = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Location location = 69;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc030ca..909efea 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -37,9 +37,6 @@
<item><xliff:g id="id">@string/status_bar_nfc</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_tty</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_speakerphone</xliff:g></item>
- <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item>
- <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
- <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_cdma_eri</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_data_connection</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item>
@@ -49,10 +46,13 @@
<item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
- <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index dce8a65..3a71851 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -19,8 +19,12 @@
<!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. -->
<zen version="7">
- <allow alarms="true" media="true" system="false" calls="false" messages="false" reminders="false"
- events="false" />
+ <allow alarms="true" media="true" system="false" calls="false" messages="false"
+ reminders="false" events="false" />
+
<!-- all visual effects that exist as of P -->
<disallow suppressedVisualEffect="511" />
+
+ <!-- whether there are notification channels that can bypass dnd -->
+ <state areChannelsBypassingDnd="false" />
</zen>
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
new file mode 100644
index 0000000..59b4665
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test cases for {@link BluetoothCodecConfig}.
+ * <p>
+ * To run this test, use:
+ * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
+ */
+public class BluetoothCodecConfigTest extends TestCase {
+ private static final int[] kCodecTypeArray = new int[] {
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID,
+ };
+ private static final int[] kCodecPriorityArray = new int[] {
+ BluetoothCodecConfig.CODEC_PRIORITY_DISABLED,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
+ };
+ private static final int[] kSampleRateArray = new int[] {
+ BluetoothCodecConfig.SAMPLE_RATE_NONE,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.SAMPLE_RATE_88200,
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.SAMPLE_RATE_176400,
+ BluetoothCodecConfig.SAMPLE_RATE_192000,
+ };
+ private static final int[] kBitsPerSampleArray = new int[] {
+ BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ };
+ private static final int[] kChannelModeArray = new int[] {
+ BluetoothCodecConfig.CHANNEL_MODE_NONE,
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ };
+ private static final long[] kCodecSpecific1Array = new long[] { 1000, 1001, 1002, 1003, };
+ private static final long[] kCodecSpecific2Array = new long[] { 2000, 2001, 2002, 2003, };
+ private static final long[] kCodecSpecific3Array = new long[] { 3000, 3001, 3002, 3003, };
+ private static final long[] kCodecSpecific4Array = new long[] { 4000, 4001, 4002, 4003, };
+
+ private static final int kTotalConfigs = kCodecTypeArray.length * kCodecPriorityArray.length *
+ kSampleRateArray.length * kBitsPerSampleArray.length * kChannelModeArray.length *
+ kCodecSpecific1Array.length * kCodecSpecific2Array.length * kCodecSpecific3Array.length *
+ kCodecSpecific4Array.length;
+
+ private int selectCodecType(int configId) {
+ int left = kCodecTypeArray.length;
+ int right = kTotalConfigs / left;
+ int index = configId / right;
+ index = index % kCodecTypeArray.length;
+ return kCodecTypeArray[index];
+ }
+
+ private int selectCodecPriority(int configId) {
+ int left = kCodecTypeArray.length * kCodecPriorityArray.length;
+ int right = kTotalConfigs / left;
+ int index = configId / right;
+ index = index % kCodecPriorityArray.length;
+ return kCodecPriorityArray[index];
+ }
+
+ private int selectSampleRate(int configId) {
+ int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length;
+ int right = kTotalConfigs / left;
+ int index = configId / right;
+ index = index % kSampleRateArray.length;
+ return kSampleRateArray[index];
+ }
+
+ private int selectBitsPerSample(int configId) {
+ int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
+ kBitsPerSampleArray.length;
+ int right = kTotalConfigs / left;
+ int index = configId / right;
+ index = index % kBitsPerSampleArray.length;
+ return kBitsPerSampleArray[index];
+ }
+
+ private int selectChannelMode(int configId) {
+ int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
+ kBitsPerSampleArray.length * kChannelModeArray.length;
+ int right = kTotalConfigs / left;
+ int index = configId / right;
+ index = index % kChannelModeArray.length;
+ return kChannelModeArray[index];
+ }
+
+ private long selectCodecSpecific1(int configId) {
+ int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
+ kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length;
+ int right = kTotalConfigs / left;
+ int index = configId / right;
+ index = index % kCodecSpecific1Array.length;
+ return kCodecSpecific1Array[index];
+ }
+
+ private long selectCodecSpecific2(int configId) {
+ int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
+ kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length *
+ kCodecSpecific2Array.length;
+ int right = kTotalConfigs / left;
+ int index = configId / right;
+ index = index % kCodecSpecific2Array.length;
+ return kCodecSpecific2Array[index];
+ }
+
+ private long selectCodecSpecific3(int configId) {
+ int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
+ kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length *
+ kCodecSpecific2Array.length * kCodecSpecific3Array.length;
+ int right = kTotalConfigs / left;
+ int index = configId / right;
+ index = index % kCodecSpecific3Array.length;
+ return kCodecSpecific3Array[index];
+ }
+
+ private long selectCodecSpecific4(int configId) {
+ int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
+ kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length *
+ kCodecSpecific2Array.length * kCodecSpecific3Array.length *
+ kCodecSpecific4Array.length;
+ int right = kTotalConfigs / left;
+ int index = configId / right;
+ index = index % kCodecSpecific4Array.length;
+ return kCodecSpecific4Array[index];
+ }
+
+ @SmallTest
+ public void testBluetoothCodecConfig_valid_get_methods() {
+
+ for (int config_id = 0; config_id < kTotalConfigs; config_id++) {
+ int codec_type = selectCodecType(config_id);
+ int codec_priority = selectCodecPriority(config_id);
+ int sample_rate = selectSampleRate(config_id);
+ int bits_per_sample = selectBitsPerSample(config_id);
+ int channel_mode = selectChannelMode(config_id);
+ long codec_specific1 = selectCodecSpecific1(config_id);
+ long codec_specific2 = selectCodecSpecific2(config_id);
+ long codec_specific3 = selectCodecSpecific3(config_id);
+ long codec_specific4 = selectCodecSpecific4(config_id);
+
+ BluetoothCodecConfig bcc = new BluetoothCodecConfig(codec_type, codec_priority,
+ sample_rate, bits_per_sample,
+ channel_mode, codec_specific1,
+ codec_specific2, codec_specific3,
+ codec_specific4);
+ if (sample_rate == BluetoothCodecConfig.SAMPLE_RATE_NONE) {
+ assertFalse(bcc.isValid());
+ } else if (bits_per_sample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) {
+ assertFalse(bcc.isValid());
+ } else if (channel_mode == BluetoothCodecConfig.CHANNEL_MODE_NONE) {
+ assertFalse(bcc.isValid());
+ } else {
+ assertTrue(bcc.isValid());
+ }
+
+ if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) {
+ assertTrue(bcc.isMandatoryCodec());
+ } else {
+ assertFalse(bcc.isMandatoryCodec());
+ }
+
+ if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) {
+ assertEquals("SBC", bcc.getCodecName());
+ }
+ if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC) {
+ assertEquals("AAC", bcc.getCodecName());
+ }
+ if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX) {
+ assertEquals("aptX", bcc.getCodecName());
+ }
+ if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD) {
+ assertEquals("aptX HD", bcc.getCodecName());
+ }
+ if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC) {
+ assertEquals("LDAC", bcc.getCodecName());
+ }
+ if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX) {
+ assertEquals("UNKNOWN CODEC(" + BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX + ")",
+ bcc.getCodecName());
+ }
+ if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
+ assertEquals("INVALID CODEC", bcc.getCodecName());
+ }
+
+ assertEquals(codec_type, bcc.getCodecType());
+ assertEquals(codec_priority, bcc.getCodecPriority());
+ assertEquals(sample_rate, bcc.getSampleRate());
+ assertEquals(bits_per_sample, bcc.getBitsPerSample());
+ assertEquals(channel_mode, bcc.getChannelMode());
+ assertEquals(codec_specific1, bcc.getCodecSpecific1());
+ assertEquals(codec_specific2, bcc.getCodecSpecific2());
+ assertEquals(codec_specific3, bcc.getCodecSpecific3());
+ assertEquals(codec_specific4, bcc.getCodecSpecific4());
+ }
+ }
+
+ @SmallTest
+ public void testBluetoothCodecConfig_equals() {
+ BluetoothCodecConfig bcc1 =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+ BluetoothCodecConfig bcc2_same =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+ assertTrue(bcc1.equals(bcc2_same));
+
+ BluetoothCodecConfig bcc3_codec_type =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+ assertFalse(bcc1.equals(bcc3_codec_type));
+
+ BluetoothCodecConfig bcc4_codec_priority =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+ assertFalse(bcc1.equals(bcc4_codec_priority));
+
+ BluetoothCodecConfig bcc5_sample_rate =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+ assertFalse(bcc1.equals(bcc5_sample_rate));
+
+ BluetoothCodecConfig bcc6_bits_per_sample =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+ assertFalse(bcc1.equals(bcc6_bits_per_sample));
+
+ BluetoothCodecConfig bcc7_channel_mode =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+ assertFalse(bcc1.equals(bcc7_channel_mode));
+
+ BluetoothCodecConfig bcc8_codec_specific1 =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1001, 2000, 3000, 4000);
+ assertFalse(bcc1.equals(bcc8_codec_specific1));
+
+ BluetoothCodecConfig bcc9_codec_specific2 =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2002, 3000, 4000);
+ assertFalse(bcc1.equals(bcc9_codec_specific2));
+
+ BluetoothCodecConfig bcc10_codec_specific3 =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3003, 4000);
+ assertFalse(bcc1.equals(bcc10_codec_specific3));
+
+ BluetoothCodecConfig bcc11_codec_specific4 =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4004);
+ assertFalse(bcc1.equals(bcc11_codec_specific4));
+ }
+}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java
new file mode 100644
index 0000000..83bf2ed
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test cases for {@link BluetoothCodecStatus}.
+ * <p>
+ * To run this test, use:
+ * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java
+ */
+public class BluetoothCodecStatusTest extends TestCase {
+
+ // Codec configs: A and B are same; C is different
+ private static final BluetoothCodecConfig config_A =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig config_B =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig config_C =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+ // Local capabilities: A and B are same; C is different
+ private static final BluetoothCodecConfig local_capability1_A =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig local_capability1_B =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig local_capability1_C =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+
+ private static final BluetoothCodecConfig local_capability2_A =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig local_capability2_B =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig local_capability2_C =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig local_capability3_A =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig local_capability3_B =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig local_capability3_C =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig local_capability4_A =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig local_capability4_B =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig local_capability4_C =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig local_capability5_A =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000 |
+ BluetoothCodecConfig.SAMPLE_RATE_88200 |
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig local_capability5_B =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000 |
+ BluetoothCodecConfig.SAMPLE_RATE_88200 |
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig local_capability5_C =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000 |
+ BluetoothCodecConfig.SAMPLE_RATE_88200 |
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+
+ // Selectable capabilities: A and B are same; C is different
+ private static final BluetoothCodecConfig selectable_capability1_A =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability1_B =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability1_C =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability2_A =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability2_B =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability2_C =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability3_A =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability3_B =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability3_C =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability4_A =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability4_B =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability4_C =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability5_A =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000 |
+ BluetoothCodecConfig.SAMPLE_RATE_88200 |
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability5_B =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000 |
+ BluetoothCodecConfig.SAMPLE_RATE_88200 |
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO |
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig selectable_capability5_C =
+ new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100 |
+ BluetoothCodecConfig.SAMPLE_RATE_48000 |
+ BluetoothCodecConfig.SAMPLE_RATE_88200 |
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 1000, 2000, 3000, 4000);
+
+ private static final BluetoothCodecConfig[] local_capability_A = {
+ local_capability1_A,
+ local_capability2_A,
+ local_capability3_A,
+ local_capability4_A,
+ local_capability5_A,
+ };
+
+ private static final BluetoothCodecConfig[] local_capability_B = {
+ local_capability1_B,
+ local_capability2_B,
+ local_capability3_B,
+ local_capability4_B,
+ local_capability5_B,
+ };
+
+ private static final BluetoothCodecConfig[] local_capability_B_reordered = {
+ local_capability5_B,
+ local_capability4_B,
+ local_capability2_B,
+ local_capability3_B,
+ local_capability1_B,
+ };
+
+ private static final BluetoothCodecConfig[] local_capability_C = {
+ local_capability1_C,
+ local_capability2_C,
+ local_capability3_C,
+ local_capability4_C,
+ local_capability5_C,
+ };
+
+ private static final BluetoothCodecConfig[] selectable_capability_A = {
+ selectable_capability1_A,
+ selectable_capability2_A,
+ selectable_capability3_A,
+ selectable_capability4_A,
+ selectable_capability5_A,
+ };
+
+ private static final BluetoothCodecConfig[] selectable_capability_B = {
+ selectable_capability1_B,
+ selectable_capability2_B,
+ selectable_capability3_B,
+ selectable_capability4_B,
+ selectable_capability5_B,
+ };
+
+ private static final BluetoothCodecConfig[] selectable_capability_B_reordered = {
+ selectable_capability5_B,
+ selectable_capability4_B,
+ selectable_capability2_B,
+ selectable_capability3_B,
+ selectable_capability1_B,
+ };
+
+ private static final BluetoothCodecConfig[] selectable_capability_C = {
+ selectable_capability1_C,
+ selectable_capability2_C,
+ selectable_capability3_C,
+ selectable_capability4_C,
+ selectable_capability5_C,
+ };
+
+ private static final BluetoothCodecStatus bcs_A =
+ new BluetoothCodecStatus(config_A, local_capability_A, selectable_capability_A);
+ private static final BluetoothCodecStatus bcs_B =
+ new BluetoothCodecStatus(config_B, local_capability_B, selectable_capability_B);
+ private static final BluetoothCodecStatus bcs_B_reordered =
+ new BluetoothCodecStatus(config_B, local_capability_B_reordered,
+ selectable_capability_B_reordered);
+ private static final BluetoothCodecStatus bcs_C =
+ new BluetoothCodecStatus(config_C, local_capability_C, selectable_capability_C);
+
+ @SmallTest
+ public void testBluetoothCodecStatus_get_methods() {
+
+ assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_A));
+ assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_B));
+ assertFalse(Objects.equals(bcs_A.getCodecConfig(), config_C));
+
+ assertTrue(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_A));
+ assertTrue(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_B));
+ assertFalse(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_C));
+
+ assertTrue(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(),
+ selectable_capability_A));
+ assertTrue(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(),
+ selectable_capability_B));
+ assertFalse(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(),
+ selectable_capability_C));
+ }
+
+ @SmallTest
+ public void testBluetoothCodecStatus_equals() {
+ assertTrue(bcs_A.equals(bcs_B));
+ assertTrue(bcs_B.equals(bcs_A));
+ assertTrue(bcs_A.equals(bcs_B_reordered));
+ assertTrue(bcs_B_reordered.equals(bcs_A));
+ assertFalse(bcs_A.equals(bcs_C));
+ assertFalse(bcs_C.equals(bcs_A));
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 196f8de..43e980e 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -240,6 +240,7 @@
Settings.Global.GLOBAL_HTTP_PROXY_HOST,
Settings.Global.GLOBAL_HTTP_PROXY_PAC,
Settings.Global.GLOBAL_HTTP_PROXY_PORT,
+ Settings.Global.GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS,
Settings.Global.GNSS_SATELLITE_BLACKLIST,
Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
diff --git a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
new file mode 100644
index 0000000..45b19bc
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.util;
+
+import static com.android.internal.util.DumpUtils.filterRecord;
+import static com.android.internal.util.DumpUtils.isNonPlatformPackage;
+import static com.android.internal.util.DumpUtils.isPlatformPackage;
+
+import android.content.ComponentName;
+
+import junit.framework.TestCase;
+
+/**
+ * Run with:
+ atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/DumpTest.java
+ */
+public class DumpUtilsTest extends TestCase {
+
+ private static ComponentName cn(String componentName) {
+ if (componentName == null) {
+ return null;
+ }
+ return ComponentName.unflattenFromString(componentName);
+ }
+
+ private static ComponentName.WithComponentName wcn(String componentName) {
+ if (componentName == null) {
+ return null;
+ }
+ return () -> cn(componentName);
+ }
+
+ public void testIsPlatformPackage() {
+ assertTrue(isPlatformPackage("android"));
+ assertTrue(isPlatformPackage("android.abc"));
+ assertTrue(isPlatformPackage("com.android.abc"));
+
+ assertFalse(isPlatformPackage((String) null));
+ assertFalse(isPlatformPackage("com.google"));
+
+ assertTrue(isPlatformPackage(cn("android/abc")));
+ assertTrue(isPlatformPackage(cn("android.abc/abc")));
+ assertTrue(isPlatformPackage(cn("com.android.def/abc")));
+
+ assertFalse(isPlatformPackage(cn(null)));
+ assertFalse(isPlatformPackage(cn("com.google.def/abc")));
+
+ assertTrue(isPlatformPackage(wcn("android/abc")));
+ assertTrue(isPlatformPackage(wcn("android.abc/abc")));
+ assertTrue(isPlatformPackage(wcn("com.android.def/abc")));
+
+ assertFalse(isPlatformPackage(wcn(null)));
+ assertFalse(isPlatformPackage(wcn("com.google.def/abc")));
+ }
+
+ public void testIsNonPlatformPackage() {
+ assertFalse(isNonPlatformPackage("android"));
+ assertFalse(isNonPlatformPackage("android.abc"));
+ assertFalse(isNonPlatformPackage("com.android.abc"));
+
+ assertFalse(isNonPlatformPackage((String) null));
+ assertTrue(isNonPlatformPackage("com.google"));
+
+ assertFalse(isNonPlatformPackage(cn("android/abc")));
+ assertFalse(isNonPlatformPackage(cn("android.abc/abc")));
+ assertFalse(isNonPlatformPackage(cn("com.android.def/abc")));
+
+ assertFalse(isNonPlatformPackage(cn(null)));
+ assertTrue(isNonPlatformPackage(cn("com.google.def/abc")));
+
+ assertFalse(isNonPlatformPackage(wcn("android/abc")));
+ assertFalse(isNonPlatformPackage(wcn("android.abc/abc")));
+ assertFalse(isNonPlatformPackage(wcn("com.android.def/abc")));
+
+ assertFalse(isNonPlatformPackage(wcn(null)));
+ assertTrue(isNonPlatformPackage(wcn("com.google.def/abc")));
+ }
+
+ public void testFilterRecord() {
+ assertFalse(filterRecord(null).test(wcn("com.google.p/abc")));
+ assertFalse(filterRecord(null).test(wcn("com.android.p/abc")));
+
+ assertTrue(filterRecord("all").test(wcn("com.google.p/abc")));
+ assertTrue(filterRecord("all").test(wcn("com.android.p/abc")));
+ assertFalse(filterRecord("all").test(wcn(null)));
+
+ assertFalse(filterRecord("all-platform").test(wcn("com.google.p/abc")));
+ assertTrue(filterRecord("all-platform").test(wcn("com.android.p/abc")));
+ assertFalse(filterRecord("all-platform").test(wcn(null)));
+
+ assertTrue(filterRecord("all-non-platform").test(wcn("com.google.p/abc")));
+ assertFalse(filterRecord("all-non-platform").test(wcn("com.android.p/abc")));
+ assertFalse(filterRecord("all-non-platform").test(wcn(null)));
+
+ // Partial string match.
+ assertTrue(filterRecord("abc").test(wcn("com.google.p/.abc")));
+ assertFalse(filterRecord("abc").test(wcn("com.google.p/.def")));
+ assertTrue(filterRecord("com").test(wcn("com.google.p/.xyz")));
+
+ // Full component name match.
+ assertTrue(filterRecord("com.google/com.google.abc").test(wcn("com.google/.abc")));
+ assertFalse(filterRecord("com.google/com.google.abc").test(wcn("com.google/.abc.def")));
+
+
+ // Hex ID match
+ ComponentName.WithComponentName component = wcn("com.google/.abc");
+
+ assertTrue(filterRecord(
+ Integer.toHexString(System.identityHashCode(component))).test(component));
+ // Same component name, but different ID, no match.
+ assertFalse(filterRecord(
+ Integer.toHexString(System.identityHashCode(component))).test(
+ wcn("com.google/.abc")));
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
new file mode 100644
index 0000000..f00c48c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.util;
+
+import junit.framework.TestCase;
+
+/**
+ * Run with:
+ atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
+ */
+public class ParseUtilsTest extends TestCase {
+ public void testParseInt() {
+ assertEquals(1, ParseUtils.parseInt(null, 1));
+ assertEquals(1, ParseUtils.parseInt("", 1));
+ assertEquals(1, ParseUtils.parseInt("1x", 1));
+ assertEquals(2, ParseUtils.parseInt("2", 1));
+
+ assertEquals(2, ParseUtils.parseInt("+2", 1));
+ assertEquals(-2, ParseUtils.parseInt("-2", 1));
+ }
+
+ public void testParseIntWithBase() {
+ assertEquals(1, ParseUtils.parseIntWithBase(null, 10, 1));
+ assertEquals(1, ParseUtils.parseIntWithBase("", 10, 1));
+ assertEquals(1, ParseUtils.parseIntWithBase("1x", 10, 1));
+ assertEquals(2, ParseUtils.parseIntWithBase("2", 10, 1));
+ assertEquals(10, ParseUtils.parseIntWithBase("10", 10, 1));
+ assertEquals(3, ParseUtils.parseIntWithBase("10", 3, 1));
+
+ assertEquals(3, ParseUtils.parseIntWithBase("+10", 3, 1));
+ assertEquals(-3, ParseUtils.parseIntWithBase("-10", 3, 1));
+ }
+
+ public void testParseLong() {
+ assertEquals(1L, ParseUtils.parseLong(null, 1));
+ assertEquals(1L, ParseUtils.parseLong("", 1));
+ assertEquals(1L, ParseUtils.parseLong("1x", 1));
+ assertEquals(2L, ParseUtils.parseLong("2", 1));
+ }
+
+ public void testParseLongWithBase() {
+ assertEquals(1L, ParseUtils.parseLongWithBase(null, 10, 1));
+ assertEquals(1L, ParseUtils.parseLongWithBase("", 10, 1));
+ assertEquals(1L, ParseUtils.parseLongWithBase("1x", 10, 1));
+ assertEquals(2L, ParseUtils.parseLongWithBase("2", 10, 1));
+ assertEquals(10L, ParseUtils.parseLongWithBase("10", 10, 1));
+ assertEquals(3L, ParseUtils.parseLongWithBase("10", 3, 1));
+
+ assertEquals(3L, ParseUtils.parseLongWithBase("+10", 3, 1));
+ assertEquals(-3L, ParseUtils.parseLongWithBase("-10", 3, 1));
+
+ assertEquals(10_000_000_000L, ParseUtils.parseLongWithBase("+10000000000", 10, 1));
+ assertEquals(-10_000_000_000L, ParseUtils.parseLongWithBase("-10000000000", 10, 1));
+
+ assertEquals(10_000_000_000L, ParseUtils.parseLongWithBase(null, 10, 10_000_000_000L));
+ }
+
+ public void testParseFloat() {
+ assertEquals(0.5f, ParseUtils.parseFloat(null, 0.5f));
+ assertEquals(0.5f, ParseUtils.parseFloat("", 0.5f));
+ assertEquals(0.5f, ParseUtils.parseFloat("1x", 0.5f));
+ assertEquals(1.5f, ParseUtils.parseFloat("1.5", 0.5f));
+ }
+
+ public void testParseDouble() {
+ assertEquals(0.5, ParseUtils.parseDouble(null, 0.5));
+ assertEquals(0.5, ParseUtils.parseDouble("", 0.5));
+ assertEquals(0.5, ParseUtils.parseDouble("1x", 0.5));
+ assertEquals(1.5, ParseUtils.parseDouble("1.5", 0.5));
+ }
+
+ public void testParseBoolean() {
+ assertEquals(false, ParseUtils.parseBoolean(null, false));
+ assertEquals(true, ParseUtils.parseBoolean(null, true));
+
+ assertEquals(false, ParseUtils.parseBoolean("", false));
+ assertEquals(true, ParseUtils.parseBoolean("", true));
+
+ assertEquals(true, ParseUtils.parseBoolean("true", false));
+ assertEquals(true, ParseUtils.parseBoolean("true", true));
+
+ assertEquals(false, ParseUtils.parseBoolean("false", false));
+ assertEquals(false, ParseUtils.parseBoolean("false", true));
+
+ assertEquals(true, ParseUtils.parseBoolean("1", false));
+ assertEquals(true, ParseUtils.parseBoolean("1", true));
+
+ assertEquals(false, ParseUtils.parseBoolean("0", false));
+ assertEquals(false, ParseUtils.parseBoolean("0", true));
+ }
+}
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 3118009..f1cc569 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -386,6 +386,38 @@
EXPECT_EQ(basic::R::array::integerArray1, last_ref);
}
+TEST_F(AssetManager2Test, ResolveDeepIdReference) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({basic_assets_.get()});
+
+ // Set up the resource ids
+ const uint32_t high_ref = assetmanager
+ .GetResourceId("@id/high_ref", "values", "com.android.basic");
+ ASSERT_NE(high_ref, 0u);
+ const uint32_t middle_ref = assetmanager
+ .GetResourceId("@id/middle_ref", "values", "com.android.basic");
+ ASSERT_NE(middle_ref, 0u);
+ const uint32_t low_ref = assetmanager
+ .GetResourceId("@id/low_ref", "values", "com.android.basic");
+ ASSERT_NE(low_ref, 0u);
+
+ // Retrieve the most shallow resource
+ Res_value value;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = assetmanager.GetResource(high_ref, false /*may_be_bag*/,
+ 0 /*density_override*/,
+ &value, &config, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+ EXPECT_EQ(middle_ref, value.data);
+
+ // Check that resolving the reference resolves to the deepest id
+ uint32_t last_ref = high_ref;
+ assetmanager.ResolveReference(cookie, &value, &config, &flags, &last_ref);
+ EXPECT_EQ(last_ref, low_ref);
+}
+
TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_.get()});
diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk
index 1733b6a..b721ebf 100644
--- a/libs/androidfw/tests/data/basic/basic.apk
+++ b/libs/androidfw/tests/data/basic/basic.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
index b343562..d4b2683 100644
--- a/libs/androidfw/tests/data/basic/res/values/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -78,4 +78,8 @@
<item type="string" name="test2" />
<item type="array" name="integerArray1" />
</overlayable>
+
+ <item name="high_ref" type="id">@id/middle_ref</item>
+ <item name="middle_ref" type="id">@id/low_ref</item>
+ <item name="low_ref" type="id"/>
</resources>
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 7888436..c486e68 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1222,7 +1222,7 @@
TAG_F_NUMBER, TAG_DIGITAL_ZOOM_RATIO, TAG_EXPOSURE_TIME, TAG_SUBJECT_DISTANCE,
TAG_GPS_TIMESTAMP));
// Mappings from tag number to IFD type for pointer tags.
- private static final HashMap sExifPointerTagMap = new HashMap();
+ private static final HashMap<Integer, Integer> sExifPointerTagMap = new HashMap();
// See JPEG File Interchange Format Version 1.02.
// The following values are defined for handling JPEG streams. In this implementation, we are
@@ -1299,6 +1299,7 @@
private final boolean mIsInputStream;
private int mMimeType;
private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
+ private Set<Integer> mAttributesOffsets = new HashSet<>(EXIF_TAGS.length);
private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN;
private boolean mHasThumbnail;
// The following values used for indicating a thumbnail position.
@@ -2957,8 +2958,9 @@
}
// See TIFF 6.0 Section 2: TIFF Structure, Figure 1.
short numberOfDirectoryEntry = dataInputStream.readShort();
- if (dataInputStream.mPosition + 12 * numberOfDirectoryEntry > dataInputStream.mLength) {
- // Return if the size of entries is too big.
+ if (dataInputStream.mPosition + 12 * numberOfDirectoryEntry > dataInputStream.mLength
+ || numberOfDirectoryEntry <= 0) {
+ // Return if the size of entries is either too big or negative.
return;
}
@@ -3049,7 +3051,7 @@
}
// Recursively parse IFD when a IFD pointer tag appears.
- Object nextIfdType = sExifPointerTagMap.get(tagNumber);
+ Integer nextIfdType = sExifPointerTagMap.get(tagNumber);
if (DEBUG) {
Log.d(TAG, "nextIfdType: " + nextIfdType + " byteCount: " + byteCount);
}
@@ -3083,9 +3085,20 @@
if (DEBUG) {
Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tag.name));
}
+
+ // Check if the next IFD offset
+ // 1. Exists within the boundaries of the input stream
+ // 2. Does not point to a previously read IFD.
if (offset > 0L && offset < dataInputStream.mLength) {
- dataInputStream.seek(offset);
- readImageFileDirectory(dataInputStream, (int) nextIfdType);
+ if (!mAttributesOffsets.contains((int) offset)) {
+ // Save offset of current IFD to prevent reading an IFD that is already read
+ mAttributesOffsets.add(dataInputStream.mPosition);
+ dataInputStream.seek(offset);
+ readImageFileDirectory(dataInputStream, nextIfdType);
+ } else {
+ Log.w(TAG, "Skip jump into the IFD since it has already been read: "
+ + "IfdType " + nextIfdType + " (at " + offset + ")");
+ }
} else {
Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
}
@@ -3127,16 +3140,27 @@
if (DEBUG) {
Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset));
}
- // The next IFD offset needs to be bigger than 8
- // since the first IFD offset is at least 8.
- if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) {
- dataInputStream.seek(nextIfdOffset);
- if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
+ // Check if the next IFD offset
+ // 1. Exists within the boundaries of the input stream
+ // 2. Does not point to a previously read IFD.
+ if (nextIfdOffset > 0L && nextIfdOffset < dataInputStream.mLength) {
+ if (!mAttributesOffsets.contains(nextIfdOffset)) {
+ // Save offset of current IFD to prevent reading an IFD that is already read.
+ mAttributesOffsets.add(dataInputStream.mPosition);
+ dataInputStream.seek(nextIfdOffset);
// Do not overwrite thumbnail IFD data if it alreay exists.
- readImageFileDirectory(dataInputStream, IFD_TYPE_THUMBNAIL);
- } else if (mAttributes[IFD_TYPE_PREVIEW].isEmpty()) {
- readImageFileDirectory(dataInputStream, IFD_TYPE_PREVIEW);
+ if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
+ readImageFileDirectory(dataInputStream, IFD_TYPE_THUMBNAIL);
+ } else if (mAttributes[IFD_TYPE_PREVIEW].isEmpty()) {
+ readImageFileDirectory(dataInputStream, IFD_TYPE_PREVIEW);
+ }
+ } else {
+ Log.w(TAG, "Stop reading file since re-reading an IFD may cause an "
+ + "infinite loop: " + nextIfdOffset);
}
+ } else {
+ Log.w(TAG, "Stop reading file since a wrong offset may cause an infinite loop: "
+ + nextIfdOffset);
}
}
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index a5f0f24..1ca3c1d 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -65,9 +65,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledLambda;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -219,7 +217,7 @@
stopScan();
mDevicesFound.clear();
mSelectedDevice = null;
- mDevicesAdapter.notifyDataSetChanged();
+ notifyDataSetChanged();
}
@Override
@@ -265,7 +263,12 @@
onReadyToShowUI();
}
mDevicesFound.add(device);
- mDevicesAdapter.notifyDataSetChanged();
+ notifyDataSetChanged();
+ }
+
+ private void notifyDataSetChanged() {
+ Handler.getMain().sendMessage(obtainMessage(
+ DevicesAdapter::notifyDataSetChanged, mDevicesAdapter));
}
//TODO also, on timeout -> call onFailure
@@ -283,7 +286,7 @@
private void onDeviceLost(@Nullable DeviceFilterPair device) {
mDevicesFound.remove(device);
- mDevicesAdapter.notifyDataSetChanged();
+ notifyDataSetChanged();
if (DEBUG) Log.i(LOG_TAG, "Lost device " + device.getDisplayName());
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java b/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java
index 9554e81..95a8f1d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java
@@ -28,6 +28,7 @@
public class CustomDialogPreference extends DialogPreference {
private CustomPreferenceDialogFragment mFragment;
+ private DialogInterface.OnShowListener mOnShowListener;
public CustomDialogPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
@@ -54,6 +55,10 @@
return mFragment != null ? mFragment.getDialog() : null;
}
+ public void setOnShowListener(DialogInterface.OnShowListener listner) {
+ mOnShowListener = listner;
+ }
+
protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
DialogInterface.OnClickListener listener) {
}
@@ -71,6 +76,10 @@
mFragment = fragment;
}
+ private DialogInterface.OnShowListener getOnShowListener() {
+ return mOnShowListener;
+ }
+
public static class CustomPreferenceDialogFragment extends PreferenceDialogFragment {
public static CustomPreferenceDialogFragment newInstance(String key) {
@@ -104,6 +113,13 @@
}
@Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.setOnShowListener(getCustomizablePreference().getOnShowListener());
+ return dialog;
+ }
+
+ @Override
public void onClick(DialogInterface dialog, int which) {
super.onClick(dialog, which);
getCustomizablePreference().onClick(dialog, which);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 547cd9a..fe0b35b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -103,10 +103,9 @@
public void handleBroadcast(Intent intent) {
String action = intent.getAction();
if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
- state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_UNKNOWN);
- enabled = state == WifiManager.WIFI_STATE_ENABLED;
+ updateWifiState();
} else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ updateWifiState();
final NetworkInfo networkInfo =
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
connected = networkInfo != null && networkInfo.isConnected();
@@ -128,6 +127,11 @@
}
}
+ private void updateWifiState() {
+ state = mWifiManager.getWifiState();
+ enabled = state == WifiManager.WIFI_STATE_ENABLED;
+ }
+
private void updateRssi(int newRssi) {
rssi = newRssi;
level = WifiManager.calculateSignalLevel(rssi, WifiManager.RSSI_LEVELS);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a2263b4..3a2d1ce 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -745,6 +745,9 @@
dumpSetting(s, p,
Settings.Global.GNSS_SATELLITE_BLACKLIST,
GlobalSettingsProto.Location.GNSS_SATELLITE_BLACKLIST);
+ dumpSetting(s, p,
+ Settings.Global.GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS,
+ GlobalSettingsProto.Location.GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS);
p.end(locationToken);
final long lpmToken = p.start(GlobalSettingsProto.LOW_POWER_MODE);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 285b89f..b5407dc 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -455,7 +455,7 @@
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true">
<intent-filter>
- <action android:name="android.intent.action.REQUEST_SLICE_PERMISSION" />
+ <action android:name="com.android.intent.action.REQUEST_SLICE_PERMISSION" />
</intent-filter>
</activity>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 8253083..ed63089 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -382,9 +382,9 @@
<!-- Instructions telling the user remaining times when enter SIM PIN view. -->
<plurals name="kg_password_default_pin_message">
- <item quantity="one">Enter SIM PIN, you have <xliff:g id="number">%d</xliff:g> remaining
+ <item quantity="one">Enter SIM PIN. You have <xliff:g id="number">%d</xliff:g> remaining
attempt before you must contact your carrier to unlock your device.</item>
- <item quantity="other">Enter SIM PIN, you have <xliff:g id="number">%d</xliff:g> remaining
+ <item quantity="other">Enter SIM PIN. You have <xliff:g id="number">%d</xliff:g> remaining
attempts.</item>
</plurals>
diff --git a/packages/SystemUI/res/layout/car_volume_dialog.xml b/packages/SystemUI/res/layout/car_volume_dialog.xml
index 94cc001..36bc85d 100644
--- a/packages/SystemUI/res/layout/car_volume_dialog.xml
+++ b/packages/SystemUI/res/layout/car_volume_dialog.xml
@@ -15,55 +15,24 @@
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/volume_dialog"
+ android:clipChildren="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/car_margin"
android:layout_marginEnd="@dimen/car_margin"
- android:background="@drawable/car_rounded_bg_bottom"
- android:theme="@style/qs_theme"
- android:clipChildren="false" >
- <LinearLayout
- android:id="@+id/volume_dialog"
+ android:theme="@style/qs_theme" >
+ <androidx.car.widget.PagedListView
+ android:id="@+id/volume_list"
+ android:background="@drawable/car_rounded_bg_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|top"
- android:orientation="vertical"
- android:clipChildren="false" >
-
- <LinearLayout
- android:id="@+id/main"
- android:layout_width="match_parent"
- android:minWidth="@dimen/volume_dialog_panel_width"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:elevation="@dimen/volume_dialog_elevation" >
- <LinearLayout
- android:id="@+id/car_volume_dialog_rows"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="vertical" >
- <!-- volume rows added and removed here! :-) -->
- </LinearLayout>
- </LinearLayout>
- </LinearLayout>
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="@dimen/car_single_line_list_item_height"
- android:gravity="center"
- android:layout_marginEnd="@dimen/car_keyline_1"
- android:layout_gravity="end">
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/expand"
- android:layout_gravity="center"
- android:layout_width="@dimen/car_primary_icon_size"
- android:layout_height="@dimen/car_primary_icon_size"
- android:src="@drawable/car_ic_arrow_drop_up"
- android:background="?android:attr/selectableItemBackground"
- android:tint="@color/car_tint"
- android:scaleType="fitCenter"
- />
- </FrameLayout>
-</FrameLayout>
\ No newline at end of file
+ android:minWidth="@dimen/volume_dialog_panel_width"
+ android:theme="?attr/dialogListTheme"
+ app:dividerStartMargin="@dimen/car_keyline_1"
+ app:dividerEndMargin="@dimen/car_keyline_1"
+ app:gutter="none"
+ app:showPagedListViewDivider="true"
+ app:scrollBarEnabled="false" />
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/car_volume_dialog_row.xml b/packages/SystemUI/res/layout/car_volume_dialog_row.xml
deleted file mode 100644
index 33cecfa..0000000
--- a/packages/SystemUI/res/layout/car_volume_dialog_row.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<!--
- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:tag="row"
- android:layout_height="@dimen/car_single_line_list_item_height"
- android:layout_width="match_parent"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:theme="@style/qs_theme">
-
- <LinearLayout
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:gravity="center"
- android:layout_gravity="center"
- android:orientation="horizontal" >
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/volume_row_icon"
- android:layout_width="@dimen/car_primary_icon_size"
- android:layout_height="@dimen/car_primary_icon_size"
- android:layout_marginStart="@dimen/car_keyline_1"
- android:background="?android:attr/selectableItemBackground"
- android:tint="@color/car_tint"
- android:scaleType="fitCenter"
- android:soundEffectsEnabled="false" />
- <SeekBar
- android:id="@+id/volume_row_slider"
- android:clickable="true"
- android:layout_marginStart="@dimen/car_keyline_1_keyline_3_diff"
- android:layout_marginEnd="@dimen/car_keyline_3"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_single_line_list_item_height"/>
- </LinearLayout>
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index dc4e255..f6c2eeb 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -87,7 +87,7 @@
android:layout_gravity="center"
android:contentDescription="@string/accessibility_volume_settings"
android:background="@drawable/ripple_drawable_20dp"
- android:tint="?android:attr/colorControlNormal"
+ android:tint="?android:attr/textColorHint"
android:soundEffectsEnabled="false" />
</FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b220091..a1e5b0e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -138,7 +138,7 @@
<!-- Vertical translation of the shelf during animation that happens after the
notification panel collapses -->
- <dimen name="shelf_appear_translation">9dp</dimen>
+ <dimen name="shelf_appear_translation">42dp</dimen>
<!-- The amount the content shifts upwards when transforming into the icon -->
<dimen name="notification_icon_transform_content_shift">32dp</dimen>
@@ -497,9 +497,6 @@
device. -->
<dimen name="unlock_move_distance">75dp</dimen>
- <!-- Distance after which the scrim starts fading in when dragging down the quick settings -->
- <dimen name="notification_scrim_wait_distance">100dp</dimen>
-
<!-- Move distance for the unlock hint animation on the lockscreen -->
<dimen name="hint_move_distance">75dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index d45c427..458e133d7 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -24,6 +24,8 @@
<item type="id" name="scale_y_animator_tag"/>
<item type="id" name="top_inset_animator_tag"/>
<item type="id" name="height_animator_tag"/>
+ <item type="id" name="x_animator_tag"/>
+ <item type="id" name="y_animator_tag"/>
<item type="id" name="shadow_alpha_animator_tag"/>
<item type="id" name="translation_x_animator_end_value_tag"/>
<item type="id" name="translation_y_animator_end_value_tag"/>
@@ -34,6 +36,8 @@
<item type="id" name="top_inset_animator_end_value_tag"/>
<item type="id" name="height_animator_end_value_tag"/>
<item type="id" name="shadow_alpha_animator_end_value_tag"/>
+ <item type="id" name="x_animator_tag_end_value"/>
+ <item type="id" name="y_animator_tag_end_value"/>
<item type="id" name="translation_x_animator_start_value_tag"/>
<item type="id" name="translation_y_animator_start_value_tag"/>
<item type="id" name="translation_z_animator_start_value_tag"/>
@@ -43,6 +47,8 @@
<item type="id" name="top_inset_animator_start_value_tag"/>
<item type="id" name="height_animator_start_value_tag"/>
<item type="id" name="shadow_alpha_animator_start_value_tag"/>
+ <item type="id" name="x_animator_tag_start_value"/>
+ <item type="id" name="y_animator_tag_start_value"/>
<item type="id" name="doze_saved_filter_tag"/>
<item type="id" name="qs_icon_tag"/>
<item type="id" name="qs_slash_tag"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 090d012..6c507be 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -515,16 +515,12 @@
<string name="accessibility_quick_settings_airplane_changed_off">Airplane mode turned off.</string>
<!-- Announcement made when the airplane mode changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_airplane_changed_on">Airplane mode turned on.</string>
- <!-- Content description of the do not disturb tile in quick settings when on in the default priority mode (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_dnd_priority_on">Do not disturb on.</string>
<!-- Content description of the do not disturb tile in quick settings when on in none (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_dnd_none_on">Do not disturb on, total silence.</string>
+ <string name="accessibility_quick_settings_dnd_none_on">total silence</string>
<!-- Content description of the do not disturb tile in quick settings when on in alarms only (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_dnd_alarms_on">Do not disturb on, alarms only.</string>
+ <string name="accessibility_quick_settings_dnd_alarms_on">alarms only</string>
<!-- Content description of the do not disturb tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_dnd">Do not disturb.</string>
- <!-- Content description of the do not disturb tile in quick settings when off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_dnd_off">Do not disturb off.</string>
<!-- Announcement made when do not disturb changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_dnd_changed_off">Do not disturb turned off.</string>
<!-- Announcement made when do not disturb changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -1586,7 +1582,7 @@
</plurals>
<string name="notification_appops_settings">Settings</string>
- <string name="notification_appops_ok">Ok</string>
+ <string name="notification_appops_ok">OK</string>
<!-- Notification: Control panel: Accessibility description for expanded inline controls view, used
to control settings about notifications related to the current notification. -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index f13be73..d22aab5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -41,7 +41,12 @@
void setInteractionState(int flags) = 4;
/**
- * Notifies SystemUI that split screen has been invoked.
- */
+ * Notifies SystemUI that split screen has been invoked.
+ */
void onSplitScreenInvoked() = 5;
+
+ /**
+ * Notifies SystemUI that Overview is shown.
+ */
+ void onOverviewShown(boolean fromHome) = 6;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index bff0d9b..8d451c1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -35,7 +35,7 @@
*/
public static final int QUICK_STEP_DRAG_SLOP_PX = convertDpToPixel(10);
public static final int QUICK_SCRUB_DRAG_SLOP_PX = convertDpToPixel(20);
- public static final int QUICK_STEP_TOUCH_SLOP_PX = convertDpToPixel(40);
+ public static final int QUICK_STEP_TOUCH_SLOP_PX = convertDpToPixel(24);
public static final int QUICK_SCRUB_TOUCH_SLOP_PX = convertDpToPixel(35);
@Retention(RetentionPolicy.SOURCE)
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 3443334..e1540ea 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -118,6 +118,19 @@
}
}
+ public void onOverviewShown(boolean fromHome) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mHandler.post(() -> {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).onOverviewShown(fromHome);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
public void setInteractionState(@InteractionType int flags) {
long token = Binder.clearCallingIdentity();
try {
@@ -306,6 +319,12 @@
}
}
+ public void notifyQuickScrubStarted() {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).onQuickScrubStarted();
+ }
+ }
+
private void updateEnabledState() {
mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
MATCH_DIRECT_BOOT_UNAWARE,
@@ -325,5 +344,7 @@
default void onConnectionChanged(boolean isConnected) {}
default void onQuickStepStarted() {}
default void onInteractionFlagsChanged(@InteractionType int flags) {}
+ default void onOverviewShown(boolean fromHome) {}
+ default void onQuickScrubStarted() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 7f7a769..f595d77 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -50,8 +50,10 @@
Key.QS_NIGHTDISPLAY_ADDED,
Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
Key.SEEN_MULTI_USER,
- Key.NUM_APPS_LAUNCHED,
- Key.HAS_SEEN_RECENTS_ONBOARDING,
+ Key.HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING,
+ Key.HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING,
+ Key.OVERVIEW_OPENED_COUNT,
+ Key.OVERVIEW_OPENED_FROM_HOME_COUNT,
Key.SEEN_RINGER_GUIDANCE_COUNT,
Key.QS_HAS_TURNED_OFF_MOBILE_DATA,
Key.TOUCHED_RINGER_TOGGLE,
@@ -88,8 +90,10 @@
*/
String QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT = "QsLongPressTooltipShownCount";
String SEEN_MULTI_USER = "HasSeenMultiUser";
- String NUM_APPS_LAUNCHED = "NumAppsLaunched";
- String HAS_SEEN_RECENTS_ONBOARDING = "HasSeenRecentsOnboarding";
+ String OVERVIEW_OPENED_COUNT = "OverviewOpenedCount";
+ String OVERVIEW_OPENED_FROM_HOME_COUNT = "OverviewOpenedFromHomeCount";
+ String HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING = "HasSeenRecentsSwipeUpOnboarding";
+ String HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING = "HasSeenRecentsQuickScrubOnboarding";
String SEEN_RINGER_GUIDANCE_COUNT = "RingerGuidanceCount";
String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed";
String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData";
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index a61ce8c..4e7c3ab 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -69,8 +69,8 @@
SystemUIFactory.createFromConfig(this);
if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
- IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
- filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
+ bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -86,11 +86,21 @@
}
}
- IntentFilter localeChangedFilter = new IntentFilter(
- Intent.ACTION_LOCALE_CHANGED);
- registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
+
}
- }, filter);
+ }, bootCompletedFilter);
+
+ IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
+ registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
+ if (!mBootCompleted) return;
+ // Update names of SystemUi notification channels
+ NotificationChannels.createAll(context);
+ }
+ }
+ }, localeChangedFilter);
} else {
// We don't need to startServices for sub-process that is doing some tasks.
// (screenshots, sweetsweetdesserts or tuner ..)
@@ -239,14 +249,4 @@
public SystemUI[] getServices() {
return mServices;
}
-
- private final BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
- // Update names of SystemUi notification channels
- NotificationChannels.createAll(context);
- }
- }
- };
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 1c98ef0..a44f9433 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -100,16 +100,6 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mQsDisabled) {
- // Only show the status bar contents in QQS header when QS is disabled.
- mHeader.measure(widthMeasureSpec, heightMeasureSpec);
- LayoutParams layoutParams = (LayoutParams) mHeader.getLayoutParams();
- int height = layoutParams.topMargin + layoutParams.bottomMargin
- + mHeader.getMeasuredHeight();
- super.onMeasure(
- widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
- return;
- }
// Since we control our own bottom, be whatever size we want.
// Otherwise the QSPanel ends up with 0 height when the window is only the
// size of the status bar.
@@ -139,8 +129,7 @@
if (disabled == mQsDisabled) return;
mQsDisabled = disabled;
mBackgroundGradient.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
- mQSPanel.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
- mQSFooter.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+ mBackground.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
}
private void updateResources() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 6c7eda7..5ae43c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -65,10 +65,9 @@
void setKeyguardShowing(boolean keyguardShowing);
/**
- * Returns the {@link View} that should expand the quick settings when clicked.
+ * Sets the {@link android.view.View.OnClickListener to be used on elements that expend QS.
*/
- @Nullable
- View getExpandView();
+ void setExpandClickListener(View.OnClickListener onClickListener);
default void disable(int state1, int state2, boolean animate) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index abe819b..fd9ddb0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -17,7 +17,6 @@
package com.android.systemui.qs;
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import android.content.Context;
import android.content.Intent;
@@ -27,6 +26,7 @@
import android.graphics.PorterDuff.Mode;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
+import android.os.Bundle;
import android.os.UserManager;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
@@ -34,6 +34,7 @@
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -96,6 +97,7 @@
private ImageView mMobileRoaming;
private final int mColorForeground;
private final CellSignalState mInfo = new CellSignalState();
+ private OnClickListener mExpandClickListener;
public QSFooterImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -140,6 +142,7 @@
mActivityStarter = Dependency.get(ActivityStarter.class);
addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
oldBottom) -> updateAnimator(right - left));
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
private void updateAnimator(int width) {
@@ -205,6 +208,11 @@
}
@Override
+ public void setExpandClickListener(OnClickListener onClickListener) {
+ mExpandClickListener = onClickListener;
+ }
+
+ @Override
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
@@ -238,8 +246,20 @@
}
@Override
- public View getExpandView() {
- return findViewById(R.id.expand_indicator);
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ if (action == AccessibilityNodeInfo.ACTION_EXPAND) {
+ if (mExpandClickListener != null) {
+ mExpandClickListener.onClick(null);
+ return true;
+ }
+ }
+ return super.performAccessibilityAction(action, arguments);
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index cb068e3..cbd1ca1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -105,6 +105,13 @@
mQSCustomizer.setEditLocation(x, y);
mQSCustomizer.restoreInstanceState(savedInstanceState);
}
+ SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this);
+ super.onDestroyView();
}
@Override
@@ -203,15 +210,13 @@
: View.INVISIBLE);
mHeader.setExpanded((mKeyguardShowing && !mHeaderAnimating)
|| (mQsExpanded && !mStackScrollerOverscrolling));
- mFooter.setVisibility((mQsExpanded || !mKeyguardShowing || mHeaderAnimating)
+ mFooter.setVisibility(
+ !mQsDisabled && (mQsExpanded || !mKeyguardShowing || mHeaderAnimating)
? View.VISIBLE
: View.INVISIBLE);
- if (mQsDisabled) {
- mFooter.setVisibility(View.GONE);
- }
mFooter.setExpanded((mKeyguardShowing && !mHeaderAnimating)
|| (mQsExpanded && !mStackScrollerOverscrolling));
- mQSPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
+ mQSPanel.setVisibility(!mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
}
public QSPanel getQsPanel() {
@@ -235,11 +240,6 @@
@Override
public void setHeaderClickable(boolean clickable) {
if (DEBUG) Log.d(TAG, "setHeaderClickable " + clickable);
-
- View expandView = mFooter.getExpandView();
- if (expandView != null) {
- expandView.setClickable(clickable);
- }
}
@Override
@@ -278,12 +278,6 @@
mHeader.setListening(listening);
mFooter.setListening(listening);
mQSPanel.setListening(mListening && mQsExpanded);
- if (listening) {
- SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
- } else {
- SysUiServiceProvider.getComponent(getContext(), CommandQueue.class)
- .removeCallbacks(this);
- }
}
@Override
@@ -370,11 +364,7 @@
@Override
public void setExpandClickListener(OnClickListener onClickListener) {
- View expandView = mFooter.getExpandView();
-
- if (expandView != null) {
- expandView.setOnClickListener(onClickListener);
- }
+ mFooter.setExpandClickListener(onClickListener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
index 24b5a34..2ea21c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
@@ -114,11 +114,9 @@
}
}
- @Nullable
@Override
- public View getExpandView() {
+ public void setExpandClickListener(OnClickListener onClickListener) {
// No view that should expand/collapse the quick settings.
- return null;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 06183e9..a25c466 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -28,6 +28,7 @@
import android.graphics.drawable.Drawable;
import android.provider.Settings;
import android.service.quicksettings.Tile;
+import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Switch;
@@ -50,6 +51,7 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
/** Quick settings tile: Bluetooth **/
public class BluetoothTile extends QSTileImpl<BooleanState> {
@@ -131,32 +133,34 @@
}
state.slash.isSlashed = !enabled;
state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
+ state.secondaryLabel = TextUtils.emptyIfNull(
+ getSecondaryLabel(enabled, connected, state.isTransient));
if (enabled) {
if (connected) {
state.icon = new BluetoothConnectedTileIcon();
- state.contentDescription = mContext.getString(
- R.string.accessibility_bluetooth_name, state.label);
-
- state.label = mController.getLastDeviceName();
+ if (!TextUtils.isEmpty(mController.getConnectedDeviceName())) {
+ state.label = mController.getConnectedDeviceName();
+ }
+ state.contentDescription =
+ mContext.getString(R.string.accessibility_bluetooth_name, state.label)
+ + ", " + state.secondaryLabel;
} else if (state.isTransient) {
state.icon = ResourceIcon.get(R.drawable.ic_bluetooth_transient_animation);
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_bluetooth_connecting);
+ state.contentDescription = state.secondaryLabel;
} else {
state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on);
state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_bluetooth_on) + ","
+ R.string.accessibility_quick_settings_bluetooth) + ","
+ mContext.getString(R.string.accessibility_not_connected);
}
state.state = Tile.STATE_ACTIVE;
} else {
state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on);
state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_bluetooth_off);
+ R.string.accessibility_quick_settings_bluetooth);
state.state = Tile.STATE_INACTIVE;
}
- state.secondaryLabel = getSecondaryLabel(enabled, connected, state.isTransient);
state.dualLabelContentDescription = mContext.getResources().getString(
R.string.accessibility_quick_settings_open_settings, getTileLabel());
state.expandedAccessibilityClassName = Switch.class.getName();
@@ -176,9 +180,18 @@
if (isTransient) {
return mContext.getString(R.string.quick_settings_bluetooth_secondary_label_transient);
}
- final CachedBluetoothDevice lastDevice = mController.getLastDevice();
- if (enabled && connected && lastDevice != null) {
+ List<CachedBluetoothDevice> connectedDevices = mController.getConnectedDevices();
+ if (enabled && connected && !connectedDevices.isEmpty()) {
+ if (connectedDevices.size() > 1) {
+ // TODO(b/76102598): add a new string for "X connected devices" after P
+ return mContext.getResources().getQuantityString(
+ R.plurals.quick_settings_hotspot_secondary_label_num_devices,
+ connectedDevices.size(),
+ connectedDevices.size());
+ }
+
+ CachedBluetoothDevice lastDevice = connectedDevices.get(0);
final int batteryLevel = lastDevice.getBatteryLevel();
if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 16c2a75..67900d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -36,6 +36,7 @@
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ZenRule;
import android.service.quicksettings.Tile;
+import android.text.TextUtils;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
@@ -223,25 +224,27 @@
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.slash.isSlashed = !state.value;
state.label = getTileLabel();
- state.secondaryLabel = ZenModeConfig.getDescription(mContext,zen != Global.ZEN_MODE_OFF,
- mController.getConfig(), false);
+ state.secondaryLabel = TextUtils.emptyIfNull(ZenModeConfig.getDescription(mContext,
+ zen != Global.ZEN_MODE_OFF, mController.getConfig(), false));
state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
switch (zen) {
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_dnd_priority_on) + ", "
+ state.contentDescription =
+ mContext.getString(R.string.accessibility_quick_settings_dnd) + ", "
+ state.secondaryLabel;
break;
case Global.ZEN_MODE_NO_INTERRUPTIONS:
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_dnd_none_on) + ", "
- + state.secondaryLabel;
+ state.contentDescription =
+ mContext.getString(R.string.accessibility_quick_settings_dnd) + ", " +
+ mContext.getString(R.string.accessibility_quick_settings_dnd_none_on)
+ + ", " + state.secondaryLabel;
break;
case ZEN_MODE_ALARMS:
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_dnd_alarms_on) + ", "
- + state.secondaryLabel;
+ state.contentDescription =
+ mContext.getString(R.string.accessibility_quick_settings_dnd) + ", " +
+ mContext.getString(R.string.accessibility_quick_settings_dnd_alarms_on)
+ + ", " + state.secondaryLabel;
break;
default:
state.contentDescription = mContext.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index 901c7ae..ffa1444 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -19,6 +19,12 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static com.android.systemui.Prefs.Key.HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING;
+import static com.android.systemui.Prefs.Key.HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING;
+import static com.android.systemui.Prefs.Key.OVERVIEW_OPENED_COUNT;
+import static com.android.systemui.Prefs.Key.OVERVIEW_OPENED_FROM_HOME_COUNT;
+
+import android.annotation.StringRes;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Context;
@@ -31,8 +37,6 @@
import android.os.Build;
import android.os.SystemProperties;
import android.os.UserManager;
-import android.text.TextUtils;
-import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -65,10 +69,16 @@
private static final boolean ONBOARDING_ENABLED = false;
private static final long SHOW_DELAY_MS = 500;
private static final long SHOW_HIDE_DURATION_MS = 300;
- // Don't show the onboarding until the user has launched this number of apps.
- private static final int SHOW_ON_APP_LAUNCH = 2;
- // After explicitly dismissing, show again after launching this number of apps.
- private static final int SHOW_ON_APP_LAUNCH_AFTER_DISMISS = 5;
+ // Show swipe-up tips after opening overview from home this number of times.
+ private static final int SWIPE_UP_SHOW_ON_OVERVIEW_OPENED_FROM_HOME_COUNT = 3;
+ // Show quick scrub tips after opening overview this number of times.
+ private static final int QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT = 10;
+ // After explicitly dismissing, show again after launching this number of apps for swipe-up
+ // tips.
+ private static final int SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS = 5;
+ // After explicitly dismissing, show again after launching this number of apps for QuickScrub
+ // tips.
+ private static final int QUICK_SCRUB_SHOW_ON_APP_LAUNCH_AFTER_DISMISS = 10;
private final Context mContext;
private final WindowManager mWindowManager;
@@ -82,11 +92,14 @@
private final int mOnboardingToastArrowRadius;
private int mNavBarHeight;
+ private boolean mOverviewProxyListenerRegistered;
private boolean mTaskListenerRegistered;
private boolean mLayoutAttachedToWindow;
private int mLastTaskId;
- private boolean mHasDismissed;
- private int mNumAppsLaunchedSinceDismiss;
+ private boolean mHasDismissedSwipeUpTip;
+ private boolean mHasDismissedQuickScrubTip;
+ private int mNumAppsLaunchedSinceSwipeUpTipDismiss;
+ private int mNumAppsLaunchedSinceQuickScrubTipDismiss;
private final SysUiTaskStackChangeListener mTaskListener = new SysUiTaskStackChangeListener() {
@Override
@@ -107,18 +120,40 @@
int activityType = info.configuration.windowConfiguration.getActivityType();
if (activityType == ACTIVITY_TYPE_STANDARD) {
mLastTaskId = info.id;
- int numAppsLaunched = mHasDismissed ? mNumAppsLaunchedSinceDismiss
- : Prefs.getInt(mContext, Prefs.Key.NUM_APPS_LAUNCHED, 0);
- int showOnAppLaunch = mHasDismissed ? SHOW_ON_APP_LAUNCH_AFTER_DISMISS
- : SHOW_ON_APP_LAUNCH;
- numAppsLaunched++;
- if (numAppsLaunched >= showOnAppLaunch) {
- show();
+
+ boolean alreadySeenSwipeUpOnboarding = hasSeenSwipeUpOnboarding();
+ boolean alreadySeenQuickScrubsOnboarding = hasSeenQuickScrubOnboarding();
+ if (alreadySeenSwipeUpOnboarding && alreadySeenQuickScrubsOnboarding) {
+ onDisconnectedFromLauncher();
+ return;
+ }
+
+ if (!alreadySeenSwipeUpOnboarding) {
+ if (getOpenedOverviewFromHomeCount()
+ >= SWIPE_UP_SHOW_ON_OVERVIEW_OPENED_FROM_HOME_COUNT) {
+ if (mHasDismissedSwipeUpTip) {
+ mNumAppsLaunchedSinceSwipeUpTipDismiss++;
+ if (mNumAppsLaunchedSinceSwipeUpTipDismiss
+ == SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS) {
+ mNumAppsLaunchedSinceSwipeUpTipDismiss = 0;
+ show(R.string.recents_swipe_up_onboarding);
+ }
+ } else {
+ show(R.string.recents_swipe_up_onboarding);
+ }
+ }
} else {
- if (mHasDismissed) {
- mNumAppsLaunchedSinceDismiss = numAppsLaunched;
- } else {
- Prefs.putInt(mContext, Prefs.Key.NUM_APPS_LAUNCHED, numAppsLaunched);
+ if (getOpenedOverviewCount() >= QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT) {
+ if (mHasDismissedQuickScrubTip) {
+ mNumAppsLaunchedSinceQuickScrubTipDismiss++;
+ if (mNumAppsLaunchedSinceQuickScrubTipDismiss
+ == QUICK_SCRUB_SHOW_ON_APP_LAUNCH_AFTER_DISMISS) {
+ mNumAppsLaunchedSinceQuickScrubTipDismiss = 0;
+ show(R.string.recents_quick_scrub_onboarding);
+ }
+ } else {
+ show(R.string.recents_quick_scrub_onboarding);
+ }
}
}
} else {
@@ -127,13 +162,36 @@
}
};
+ private OverviewProxyService.OverviewProxyListener mOverviewProxyListener =
+ new OverviewProxyService.OverviewProxyListener() {
+ @Override
+ public void onOverviewShown(boolean fromHome) {
+ boolean alreadySeenRecentsOnboarding = hasSeenSwipeUpOnboarding();
+ if (!alreadySeenRecentsOnboarding && !fromHome) {
+ setHasSeenSwipeUpOnboarding(true);
+ }
+ if (fromHome) {
+ setOpenedOverviewFromHomeCount(getOpenedOverviewFromHomeCount() + 1);
+ }
+ setOpenedOverviewCount(getOpenedOverviewCount() + 1);
+ }
+
+ @Override
+ public void onQuickScrubStarted() {
+ boolean alreadySeenQuickScrubsOnboarding = hasSeenQuickScrubOnboarding();
+ if (!alreadySeenQuickScrubsOnboarding) {
+ setHasSeenQuickScrubOnboarding(true);
+ }
+ }
+ };
+
private final View.OnAttachStateChangeListener mOnAttachStateChangeListener
= new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {
if (view == mLayout) {
mLayoutAttachedToWindow = true;
- mHasDismissed = false;
+ mHasDismissedSwipeUpTip = false;
}
}
@@ -167,8 +225,19 @@
mLayout.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
mDismissView.setOnClickListener(v -> {
hide(true);
- mHasDismissed = true;
- mNumAppsLaunchedSinceDismiss = 0;
+ if (v.getTag().equals(R.string.recents_swipe_up_onboarding)) {
+ mHasDismissedSwipeUpTip = true;
+ mNumAppsLaunchedSinceSwipeUpTipDismiss = 0;
+ } else {
+ if (mHasDismissedQuickScrubTip) {
+ // If user dismisses the quick scrub tip twice, we consider user has seen it
+ // and do not show it again.
+ setHasSeenQuickScrubOnboarding(true);
+ } else {
+ mHasDismissedQuickScrubTip = true;
+ }
+ mNumAppsLaunchedSinceQuickScrubTipDismiss = 0;
+ }
});
ViewGroup.LayoutParams arrowLp = mArrowView.getLayoutParams();
@@ -181,8 +250,10 @@
mArrowView.setBackground(arrowDrawable);
if (RESET_PREFS_FOR_DEBUG) {
- Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_RECENTS_ONBOARDING, false);
- Prefs.putInt(mContext, Prefs.Key.NUM_APPS_LAUNCHED, 0);
+ setHasSeenSwipeUpOnboarding(false);
+ setHasSeenQuickScrubOnboarding(false);
+ setOpenedOverviewCount(0);
+ setOpenedOverviewFromHomeCount(0);
}
}
@@ -190,30 +261,35 @@
if (!ONBOARDING_ENABLED) {
return;
}
- boolean alreadySeenRecentsOnboarding = Prefs.getBoolean(mContext,
- Prefs.Key.HAS_SEEN_RECENTS_ONBOARDING, false);
- if (!mTaskListenerRegistered && !alreadySeenRecentsOnboarding) {
+
+ if (hasSeenSwipeUpOnboarding() && hasSeenQuickScrubOnboarding()) {
+ return;
+ }
+
+ if (!mOverviewProxyListenerRegistered) {
+ mOverviewProxyService.addCallback(mOverviewProxyListener);
+ mOverviewProxyListenerRegistered = true;
+ }
+ if (!mTaskListenerRegistered) {
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
mTaskListenerRegistered = true;
}
}
- public void onQuickStepStarted() {
- boolean alreadySeenRecentsOnboarding = Prefs.getBoolean(mContext,
- Prefs.Key.HAS_SEEN_RECENTS_ONBOARDING, false);
- if (!alreadySeenRecentsOnboarding) {
- Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_RECENTS_ONBOARDING, true);
- onDisconnectedFromLauncher();
- }
- }
-
public void onDisconnectedFromLauncher() {
+ if (mOverviewProxyListenerRegistered) {
+ mOverviewProxyService.removeCallback(mOverviewProxyListener);
+ mOverviewProxyListenerRegistered = false;
+ }
if (mTaskListenerRegistered) {
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskListener);
mTaskListenerRegistered = false;
}
- mHasDismissed = false;
- mNumAppsLaunchedSinceDismiss = 0;
+
+ mHasDismissedSwipeUpTip = false;
+ mHasDismissedQuickScrubTip = false;
+ mNumAppsLaunchedSinceSwipeUpTipDismiss = 0;
+ mNumAppsLaunchedSinceQuickScrubTipDismiss = 0;
hide(false);
}
@@ -223,11 +299,12 @@
}
}
- public void show() {
+ public void show(@StringRes int stringRes) {
if (!shouldShow()) {
return;
}
- mTextView.setText(R.string.recents_swipe_up_onboarding);
+ mDismissView.setTag(stringRes);
+ mTextView.setText(stringRes);
// Only show in portrait.
int orientation = mContext.getResources().getConfiguration().orientation;
if (!mLayoutAttachedToWindow && orientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -259,7 +336,7 @@
private boolean shouldShow() {
return SystemProperties.getBoolean("persist.quickstep.onboarding.enabled",
!(mContext.getSystemService(UserManager.class)).isDemoUser() &&
- !ActivityManager.isRunningInTestHarness());
+ !ActivityManager.isRunningInTestHarness());
}
public void hide(boolean animate) {
@@ -299,4 +376,43 @@
lp.gravity = Gravity.BOTTOM;
return lp;
}
+
+ private boolean hasSeenSwipeUpOnboarding() {
+ return Prefs.getBoolean(mContext, HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING, false);
+ }
+
+ private void setHasSeenSwipeUpOnboarding(boolean hasSeenSwipeUpOnboarding) {
+ Prefs.putBoolean(mContext, HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING, hasSeenSwipeUpOnboarding);
+ if (hasSeenSwipeUpOnboarding && hasSeenQuickScrubOnboarding()) {
+ onDisconnectedFromLauncher();
+ }
+ }
+
+ private boolean hasSeenQuickScrubOnboarding() {
+ return Prefs.getBoolean(mContext, HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING, false);
+ }
+
+ private void setHasSeenQuickScrubOnboarding(boolean hasSeenQuickScrubOnboarding) {
+ Prefs.putBoolean(mContext, HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING,
+ hasSeenQuickScrubOnboarding);
+ if (hasSeenQuickScrubOnboarding && hasSeenSwipeUpOnboarding()) {
+ onDisconnectedFromLauncher();
+ }
+ }
+
+ private int getOpenedOverviewFromHomeCount() {
+ return Prefs.getInt(mContext, OVERVIEW_OPENED_FROM_HOME_COUNT, 0);
+ }
+
+ private void setOpenedOverviewFromHomeCount(int openedOverviewFromHomeCount) {
+ Prefs.putInt(mContext, OVERVIEW_OPENED_FROM_HOME_COUNT, openedOverviewFromHomeCount);
+ }
+
+ private int getOpenedOverviewCount() {
+ return Prefs.getInt(mContext, OVERVIEW_OPENED_COUNT, 0);
+ }
+
+ private void setOpenedOverviewCount(int openedOverviewCount) {
+ Prefs.putInt(mContext, OVERVIEW_OPENED_COUNT, openedOverviewCount);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index bfbba7c..3dd6e35 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -107,7 +107,7 @@
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 4388b41..011be88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -62,6 +62,10 @@
mEmptyText.setText(mText);
}
+ public int getTextResource() {
+ return mText;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 37d92d92..b442bb4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -519,6 +519,14 @@
return null;
}
+ public int getRank(String key) {
+ if (mRankingMap != null) {
+ getRanking(key, mTmpRanking);
+ return mTmpRanking.getRank();
+ }
+ return 0;
+ }
+
public boolean shouldHide(String key) {
if (mRankingMap != null) {
getRanking(key, mTmpRanking);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 6437a3a..3a79e70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -46,6 +46,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dependency;
@@ -367,6 +368,10 @@
}
public void performRemoveNotification(StatusBarNotification n) {
+ final int rank = mNotificationData.getRank(n.getKey());
+ final int count = mNotificationData.getActiveNotifications().size();
+ final NotificationVisibility nv = NotificationVisibility.obtain(n.getKey(), rank, count,
+ true);
NotificationData.Entry entry = mNotificationData.get(n.getKey());
mRemoteInputManager.onPerformRemoveNotification(n, entry);
final String pkg = n.getPackageName();
@@ -380,7 +385,7 @@
} else if (mListContainer.hasPulsingNotifications()) {
dismissalSurface = NotificationStats.DISMISSAL_AOD;
}
- mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(), dismissalSurface);
+ mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(), dismissalSurface, nv);
removeNotification(n.getKey(), null);
} catch (RemoteException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index e24bf67..c4cc494 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -38,6 +38,7 @@
import android.widget.Toast;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
@@ -129,8 +130,13 @@
}
}
if (notificationKey != null) {
+ final int count =
+ mEntryManager.getNotificationData().getActiveNotifications().size();
+ final int rank = mEntryManager.getNotificationData().getRank(notificationKey);
+ final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
+ rank, count, true);
try {
- mBarService.onNotificationClick(notificationKey);
+ mBarService.onNotificationClick(notificationKey, nv);
} catch (RemoteException e) {
/* ignore */
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
index 4225f83..01ec461 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
@@ -107,7 +107,7 @@
NotificationData.Entry entry = activeNotifications.get(i);
String key = entry.notification.getKey();
boolean isVisible = mListContainer.isInVisibleLocation(entry.row);
- NotificationVisibility visObj = NotificationVisibility.obtain(key, i, isVisible);
+ NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
if (isVisible) {
// Build new set of visible notifications.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 3c480d8..a333654 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -39,6 +39,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -132,8 +133,11 @@
ViewGroup actionGroup = (ViewGroup) parent;
index = actionGroup.indexOfChild(view);
}
+ final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
+ final int rank = mEntryManager.getNotificationData().getRank(key);
+ final NotificationVisibility nv = NotificationVisibility.obtain(key, rank, count, true);
try {
- mBarService.onNotificationActionClick(key, index);
+ mBarService.onNotificationActionClick(key, index, nv);
} catch (RemoteException e) {
// Ignore
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 6364f5b..44136c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -58,7 +58,7 @@
= SystemProperties.getBoolean("debug.icon_scroll_animations", true);
private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag;
private static final String TAG = "NotificationShelf";
- private static final long SHELF_IN_TRANSLATION_DURATION = 220;
+ private static final long SHELF_IN_TRANSLATION_DURATION = 200;
private ViewInvertHelper mViewInvertHelper;
private boolean mDark;
@@ -157,14 +157,18 @@
public void fadeInTranslating() {
float translation = mShelfIcons.getTranslationY();
- mShelfIcons.setTranslationY(translation + mShelfAppearTranslation);
+ mShelfIcons.setTranslationY(translation - mShelfAppearTranslation);
mShelfIcons.setAlpha(0);
mShelfIcons.animate()
- .alpha(1)
- .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+ .setInterpolator(Interpolators.DECELERATE_QUINT)
.translationY(translation)
.setDuration(SHELF_IN_TRANSLATION_DURATION)
.start();
+ mShelfIcons.animate()
+ .alpha(1)
+ .setInterpolator(Interpolators.LINEAR)
+ .setDuration(SHELF_IN_TRANSLATION_DURATION)
+ .start();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
index 14a6c42..b2eb18e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar;
+import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.util.AttributeSet;
@@ -35,6 +36,7 @@
private boolean mIsVisible;
private boolean mIsSecondaryVisible;
private boolean mAnimating;
+ private boolean mSecondaryAnimating;
private int mDuration = 260;
public StackScrollerDecorView(Context context, AttributeSet attrs) {
@@ -61,13 +63,26 @@
}
public void performVisibilityAnimation(boolean nowVisible) {
- animateText(mContent, nowVisible, null /* onFinishedRunnable */);
- mIsVisible = nowVisible;
+ performVisibilityAnimation(nowVisible, null /* onFinishedRunnable */);
}
public void performVisibilityAnimation(boolean nowVisible, Runnable onFinishedRunnable) {
- animateText(mContent, nowVisible, onFinishedRunnable);
- mIsVisible = nowVisible;
+ boolean oldVisible = isVisible();
+ animateText(mContent, nowVisible, oldVisible, new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mAnimating = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimating = false;
+ mIsVisible = nowVisible;
+ if (onFinishedRunnable != null) {
+ onFinishedRunnable.run();
+ }
+ }
+ });
}
public void performSecondaryVisibilityAnimation(boolean nowVisible) {
@@ -76,16 +91,43 @@
public void performSecondaryVisibilityAnimation(boolean nowVisible,
Runnable onFinishedRunnable) {
- animateText(mSecondaryView, nowVisible, onFinishedRunnable);
- mIsSecondaryVisible = nowVisible;
+ boolean oldVisible = isSecondaryVisible();
+ animateText(mSecondaryView, nowVisible, oldVisible, new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mSecondaryAnimating = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mSecondaryAnimating = false;
+ mIsSecondaryVisible = nowVisible;
+ if (onFinishedRunnable != null) {
+ onFinishedRunnable.run();
+ }
+ }
+ });
}
+ /**
+ * Check whether the secondary view is visible or not.<p/>
+ *
+ * @see #isVisible()
+ */
public boolean isSecondaryVisible() {
- return mSecondaryView != null && (mIsSecondaryVisible || mAnimating);
+ return mSecondaryView != null && (mIsSecondaryVisible ^ mSecondaryAnimating);
}
+ /**
+ * Check whether the whole view is visible or not.<p/>
+ * The view is considered visible if it matches one of following:
+ * <ul>
+ * <li> It's visible and there is no ongoing animation. </li>
+ * <li> It's not visible but is animating, thus being eventually visible. </li>
+ * </ul>
+ */
public boolean isVisible() {
- return mIsVisible || mAnimating;
+ return mIsVisible ^ mAnimating;
}
void setDuration(int duration) {
@@ -95,15 +137,18 @@
/**
* Animate the text to a new visibility.
*
- * @param nowVisible should it now be visible
- * @param onFinishedRunnable A runnable which should be run when the animation is
- * finished.
+ * @param view Target view, maybe content view or dissmiss view
+ * @param nowVisible Should it now be visible
+ * @param oldVisible Is it visible currently
+ * @param listener A listener that doing flag settings or other actions
*/
- private void animateText(View view, boolean nowVisible, final Runnable onFinishedRunnable) {
+ private void animateText(View view, boolean nowVisible, boolean oldVisible,
+ AnimatorListenerAdapter listener) {
if (view == null) {
return;
}
- if (nowVisible != mIsVisible) {
+
+ if (nowVisible != oldVisible) {
// Animate text
float endValue = nowVisible ? 1.0f : 0.0f;
Interpolator interpolator;
@@ -112,24 +157,11 @@
} else {
interpolator = Interpolators.ALPHA_OUT;
}
- mAnimating = true;
view.animate()
.alpha(endValue)
.setInterpolator(interpolator)
.setDuration(mDuration)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mAnimating = false;
- if (onFinishedRunnable != null) {
- onFinishedRunnable.run();
- }
- }
- });
- } else {
- if (onFinishedRunnable != null) {
- onFinishedRunnable.run();
- }
+ .setListener(listener);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index e7c8c94..8160f90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -75,7 +75,8 @@
int displayId = getDisplayId();
for (ActivityManager.StackInfo stackInfo :stackInfoList) {
// if the display id is known and does not match the stack we skip
- if (displayId != -1 && displayId != stackInfo.displayId) {
+ if (displayId != -1 && displayId != stackInfo.displayId ||
+ stackInfo.topActivity == null) {
continue;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index 7d283d9..81d6191 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -28,8 +28,10 @@
import android.os.IBinder;
import android.util.Log;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -46,7 +48,7 @@
private Handler mHandler;
private Car mCar;
private CarHvacManager mHvacManager;
- private HashMap<HvacKey, TemperatureView> mTempComponents = new HashMap<>();
+ private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
public HvacController(Context context) {
mContext = context;
@@ -114,18 +116,24 @@
* @param temperatureView
*/
public void addHvacTextView(TemperatureView temperatureView) {
- mTempComponents.put(
- new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId()),
- temperatureView);
+
+ HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
+ if (!mTempComponents.containsKey(hvacKey)) {
+ mTempComponents.put(hvacKey, new ArrayList<>());
+ }
+ mTempComponents.get(hvacKey).add(temperatureView);
initComponent(temperatureView);
}
private void initComponents() {
- Iterator<Map.Entry<HvacKey, TemperatureView>> iterator =
+ Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
mTempComponents.entrySet().iterator();
while (iterator.hasNext()) {
- Map.Entry<HvacKey, TemperatureView> next = iterator.next();
- initComponent(next.getValue());
+ Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
+ List<TemperatureView> temperatureViews = next.getValue();
+ for (TemperatureView view : temperatureViews) {
+ initComponent(view);
+ }
}
}
@@ -155,11 +163,13 @@
try {
int areaId = val.getAreaId();
int propertyId = val.getPropertyId();
- TemperatureView temperatureView = mTempComponents.get(
+ List<TemperatureView> temperatureViews = mTempComponents.get(
new HvacKey(propertyId, areaId));
- if (temperatureView != null) {
+ if (temperatureViews != null && !temperatureViews.isEmpty()) {
float value = (float) val.getValue();
- temperatureView.setTemp(value);
+ for (TemperatureView tempView : temperatureViews) {
+ tempView.setTemp(value);
+ }
} // else the data is not of interest
} catch (Exception e) {
// catch all so we don't take down the sysui if a new data type is
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
index d7b211f..75b41ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
@@ -20,25 +20,30 @@
import android.util.Property;
import android.view.View;
-import com.android.systemui.statusbar.stack.AnimationProperties;
+import com.android.systemui.R;
import java.util.function.BiConsumer;
-import java.util.function.Consumer;
import java.util.function.Function;
/**
* An animatable property of a view. Used with {@link PropertyAnimator}
*/
-public interface AnimatableProperty {
- int getAnimationStartTag();
+public abstract class AnimatableProperty {
- int getAnimationEndTag();
+ public static final AnimatableProperty X = AnimatableProperty.from(View.X,
+ R.id.x_animator_tag, R.id.x_animator_tag_start_value, R.id.x_animator_tag_end_value);
+ public static final AnimatableProperty Y = AnimatableProperty.from(View.Y,
+ R.id.y_animator_tag, R.id.y_animator_tag_start_value, R.id.y_animator_tag_end_value);
- int getAnimatorTag();
+ public abstract int getAnimationStartTag();
- Property getProperty();
+ public abstract int getAnimationEndTag();
- static <T extends View> AnimatableProperty from(String name, BiConsumer<T, Float> setter,
+ public abstract int getAnimatorTag();
+
+ public abstract Property getProperty();
+
+ public static <T extends View> AnimatableProperty from(String name, BiConsumer<T, Float> setter,
Function<T, Float> getter, int animatorTag, int startValueTag, int endValueTag) {
Property<T, Float> property = new FloatProperty<T>(name) {
@@ -74,4 +79,29 @@
}
};
}
+
+ public static <T extends View> AnimatableProperty from(Property<T, Float> property,
+ int animatorTag, int startValueTag, int endValueTag) {
+ return new AnimatableProperty() {
+ @Override
+ public int getAnimationStartTag() {
+ return startValueTag;
+ }
+
+ @Override
+ public int getAnimationEndTag() {
+ return endValueTag;
+ }
+
+ @Override
+ public int getAnimatorTag() {
+ return animatorTag;
+ }
+
+ @Override
+ public Property getProperty() {
+ return property;
+ }
+ };
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
index 92dcc9e..2efcd16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
@@ -24,6 +24,7 @@
import android.view.View;
import android.view.animation.Interpolator;
+import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.Interpolators;
import com.android.systemui.statusbar.stack.AnimationFilter;
import com.android.systemui.statusbar.stack.AnimationProperties;
@@ -115,4 +116,7 @@
view.setTag(animationEndTag, newEndValue);
}
+ public static <T extends View> boolean isAnimating(T view, AnimatableProperty property) {
+ return view.getTag(property.getAnimatorTag()) != null;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index d9a55c5..042e4ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -20,8 +20,8 @@
import android.content.res.Resources;
import android.util.MathUtils;
-import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -47,12 +47,6 @@
private int mClockNotificationsMargin;
/**
- * Current height of {@link NotificationPanelView}, considering how much the
- * user collapsed it.
- */
- private float mExpandedHeight;
-
- /**
* Height of the parent view - display size in px.
*/
private int mHeight;
@@ -84,9 +78,9 @@
private int mContainerTopPadding;
/**
- * @see NotificationPanelView#getMaxPanelHeight()
+ * @see NotificationPanelView#getExpandedFraction()
*/
- private float mMaxPanelHeight;
+ private float mPanelExpansion;
/**
* Burn-in prevention x translation.
@@ -140,13 +134,13 @@
}
public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
- float expandedHeight, float maxPanelHeight, int parentHeight, int keyguardStatusHeight,
- float dark, boolean secure, boolean pulsing, int bouncerTop) {
+ float panelExpansion, int parentHeight,
+ int keyguardStatusHeight, float dark, boolean secure, boolean pulsing,
+ int bouncerTop) {
mMinTopMargin = minTopMargin + mContainerTopPadding;
mMaxShadeBottom = maxShadeBottom;
mNotificationStackHeight = notificationStackHeight;
- mExpandedHeight = expandedHeight;
- mMaxPanelHeight = maxPanelHeight;
+ mPanelExpansion = panelExpansion;
mHeight = parentHeight;
mKeyguardStatusHeight = keyguardStatusHeight;
mDarkAmount = dark;
@@ -171,16 +165,12 @@
return mHeight / 2 - mKeyguardStatusHeight - mClockNotificationsMargin;
}
- public int getExpandedClockBottom() {
- return getExpandedClockPosition() + mKeyguardStatusHeight;
- }
-
/**
* Vertically align the clock and the shade in the available space considering only
* a percentage of the clock height defined by {@code CLOCK_HEIGHT_WEIGHT}.
* @return Clock Y in pixels.
*/
- private int getExpandedClockPosition() {
+ public int getExpandedClockPosition() {
final int availableHeight = mMaxShadeBottom - mMinTopMargin;
final int containerCenter = mMinTopMargin + availableHeight / 2;
@@ -212,8 +202,7 @@
mMinTopMargin : -mKeyguardStatusHeight;
// Move clock up while collapsing the shade
- float shadeExpansion = mExpandedHeight / mMaxPanelHeight;
- shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(shadeExpansion);
+ float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(mPanelExpansion);
final float clockY = MathUtils.lerp(clockYTarget, clockYRegular, shadeExpansion);
return (int) MathUtils.lerp(clockY, clockYDark, mDarkAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index ca6d596..66176b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -89,7 +89,6 @@
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
@@ -174,14 +173,10 @@
public void onConnectionChanged(boolean isConnected) {
mNavigationBarView.updateStates();
updateScreenPinningGestures();
- WindowManagerWrapper.getInstance()
- .setNavBarVirtualKeyHapticFeedbackEnabled(!isConnected);
}
@Override
public void onQuickStepStarted() {
- mNavigationBarView.onQuickStepStarted();
-
// Use navbar dragging as a signal to hide the rotate button
setRotateSuggestionButtonState(false);
}
@@ -549,7 +544,12 @@
// Set visibility, may fail if a11y service is active.
// If invisible, call will stop animation.
- mNavigationBarView.setRotateButtonVisibility(true);
+ int appliedVisibility = mNavigationBarView.setRotateButtonVisibility(true);
+ if (appliedVisibility == View.VISIBLE) {
+ // If the button will actually become visible and the navbar is about to hide,
+ // tell the statusbar to keep it around for longer
+ mStatusBar.touchAutoHide();
+ }
} else { // Hide
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index d79f308..6dbe9f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -67,6 +67,7 @@
import com.android.systemui.recents.RecentsOnboarding;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.NavigationBarCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.policy.DeadZone;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
@@ -286,12 +287,6 @@
notifyVerticalChangedListener(mVertical);
}
- public void onQuickStepStarted() {
- if (mRecentsOnboarding != null) {
- mRecentsOnboarding.onQuickStepStarted();
- }
- }
-
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mDeadZone.onTouchEvent(event)) {
@@ -682,6 +677,8 @@
reloadNavIcons();
updateNavButtonIcons();
setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
+ WindowManagerWrapper.getInstance().setNavBarVirtualKeyHapticFeedbackEnabled(
+ !mOverviewProxyService.shouldShowSwipeUpUI());
}
private void updateSlippery() {
@@ -769,13 +766,13 @@
if (setIcon) getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
}
- public void setRotateButtonVisibility(final boolean visible) {
+ public int setRotateButtonVisibility(final boolean visible) {
// Never show if a11y is visible
final boolean adjVisible = visible && !mShowAccessibilityButton;
final int vis = adjVisible ? View.VISIBLE : View.INVISIBLE;
// No need to do anything if the request matches the current state
- if (vis == getRotateSuggestionButton().getVisibility()) return;
+ if (vis == getRotateSuggestionButton().getVisibility()) return vis;
getRotateSuggestionButton().setVisibility(vis);
mShowRotateButton = visible;
@@ -792,6 +789,9 @@
// Hide/restore other button visibility, if necessary
updateNavButtonIcons();
+
+ // Return applied visibility
+ return vis;
}
public boolean isRotateButtonVisible() { return mShowRotateButton; }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 351633b..a0a97c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -20,7 +20,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
@@ -44,7 +43,6 @@
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
@@ -69,8 +67,11 @@
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.stack.AnimationProperties;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -101,6 +102,8 @@
public static final long DOZE_ANIMATION_DURATION = 700;
+ private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = new AnimationProperties()
+ .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
private static final FloatProperty<NotificationPanelView> SET_DARK_AMOUNT_PROPERTY =
new FloatProperty<NotificationPanelView>("mDarkAmount") {
@Override
@@ -122,11 +125,10 @@
private QS mQs;
private FrameLayout mQsFrame;
private KeyguardStatusView mKeyguardStatusView;
- private View mReserveNotificationSpace;
private View mQsNavbarScrim;
protected NotificationsQuickSettingsContainer mNotificationContainerParent;
protected NotificationStackScrollLayout mNotificationStackScroller;
- private boolean mAnimateNextTopPaddingChange;
+ private boolean mAnimateNextPositionUpdate;
private int mTrackingPointer;
private VelocityTracker mQsVelocityTracker;
@@ -173,9 +175,6 @@
private int mUnlockMoveDistance;
private float mEmptyDragAmount;
- private Animator mClockAnimator;
- private int mClockAnimationTargetX = Integer.MIN_VALUE;
- private int mClockAnimationTargetY = Integer.MIN_VALUE;
private KeyguardClockPositionAlgorithm mClockPositionAlgorithm =
new KeyguardClockPositionAlgorithm();
private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
@@ -183,7 +182,6 @@
private boolean mIsExpanding;
private boolean mBlockTouches;
- private int mNotificationScrimWaitDistance;
// Used for two finger gesture as well as accessibility shortcut to QS.
private boolean mQsExpandImmediate;
private boolean mTwoFingerQsExpandPossible;
@@ -310,8 +308,6 @@
getResources().getDimensionPixelSize(R.dimen.header_notifications_collide_distance);
mUnlockMoveDistance = getResources().getDimensionPixelOffset(R.dimen.unlock_move_distance);
mClockPositionAlgorithm.loadDimens(getResources());
- mNotificationScrimWaitDistance =
- getResources().getDimensionPixelSize(R.dimen.notification_scrim_wait_distance);
mQsFalsingThreshold = getResources().getDimensionPixelSize(
R.dimen.qs_falsing_threshold);
mPositionMinSideMargin = getResources().getDimensionPixelSize(
@@ -461,19 +457,19 @@
*/
private void positionClockAndNotifications() {
boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
+ boolean animateClock = animate || mAnimateNextPositionUpdate;
int stackScrollerPadding;
if (mStatusBarState != StatusBarState.KEYGUARD) {
stackScrollerPadding = (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
+ mQsNotificationTopPadding;
} else {
- final int totalHeight = getHeight();
- final int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
+ int totalHeight = getHeight();
+ int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
mClockPositionAlgorithm.setup(
mStatusBarMinHeight,
totalHeight - bottomPadding,
- calculatePanelHeightShade() - mNotificationStackScroller.getTopPadding(),
- getExpandedHeight(),
- getMaxPanelHeight(),
+ mNotificationStackScroller.getIntrinsicContentHeight(),
+ getExpandedFraction(),
totalHeight,
mKeyguardStatusView.getHeight(),
mDarkAmount,
@@ -481,12 +477,10 @@
mPulsing,
mBouncerTop);
mClockPositionAlgorithm.run(mClockPositionResult);
- if (animate || mClockAnimator != null) {
- startClockAnimation(mClockPositionResult.clockX, mClockPositionResult.clockY);
- } else {
- mKeyguardStatusView.setX(mClockPositionResult.clockX);
- mKeyguardStatusView.setY(mClockPositionResult.clockY);
- }
+ PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
+ mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
+ PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
+ mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
updateClock();
stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
}
@@ -497,6 +491,7 @@
mStackScrollerMeasuringPass++;
requestScrollerTopPaddingUpdate(animate);
mStackScrollerMeasuringPass = 0;
+ mAnimateNextPositionUpdate = false;
}
/**
@@ -558,42 +553,6 @@
positionClockAndNotifications();
}
- private void startClockAnimation(int x, int y) {
- if (mClockAnimationTargetX == x && mClockAnimationTargetY == y) {
- return;
- }
- mClockAnimationTargetX = x;
- mClockAnimationTargetY = y;
- getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getViewTreeObserver().removeOnPreDrawListener(this);
- if (mClockAnimator != null) {
- mClockAnimator.removeAllListeners();
- mClockAnimator.cancel();
- }
- AnimatorSet set = new AnimatorSet();
- set.play(ObjectAnimator.ofFloat(
- mKeyguardStatusView, View.Y, mClockAnimationTargetY))
- .with(ObjectAnimator.ofFloat(
- mKeyguardStatusView, View.X, mClockAnimationTargetX));
- mClockAnimator = set;
- mClockAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mClockAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- mClockAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mClockAnimator = null;
- mClockAnimationTargetX = Integer.MIN_VALUE;
- mClockAnimationTargetY = Integer.MIN_VALUE;
- }
- });
- mClockAnimator.start();
- return true;
- }
- });
- }
-
private void updateClock() {
if (!mKeyguardStatusViewAnimating) {
mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha);
@@ -601,9 +560,9 @@
}
public void animateToFullShade(long delay) {
- mAnimateNextTopPaddingChange = true;
mNotificationStackScroller.goToFullShade(delay);
requestLayout();
+ mAnimateNextPositionUpdate = true;
}
public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
@@ -1411,10 +1370,8 @@
protected void requestScrollerTopPaddingUpdate(boolean animate) {
mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(),
- mAnimateNextTopPaddingChange || animate,
- mKeyguardShowing
+ animate, mKeyguardShowing
&& (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted));
- mAnimateNextTopPaddingChange = false;
}
private void trackMovement(MotionEvent event) {
@@ -1535,7 +1492,7 @@
if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) {
maxHeight = calculatePanelHeightQsExpanded();
} else {
- maxHeight = Math.max(calculatePanelHeightShade(), calculatePanelHeightShadeExpanded());
+ maxHeight = calculatePanelHeightShade();
}
maxHeight = Math.max(maxHeight, min);
return maxHeight;
@@ -1606,14 +1563,15 @@
int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin;
maxHeight += mNotificationStackScroller.getTopPaddingOverflow();
- return maxHeight;
- }
- private int calculatePanelHeightShadeExpanded() {
- return mNotificationStackScroller.getHeight()
- - mNotificationStackScroller.getEmptyBottomMargin()
- - mNotificationStackScroller.getTopPadding()
- + mClockPositionAlgorithm.getExpandedClockBottom();
+ if (mStatusBarState == StatusBarState.KEYGUARD) {
+ int minKeyguardPanelBottom = mClockPositionAlgorithm.getExpandedClockPosition()
+ + mKeyguardStatusView.getHeight()
+ + mNotificationStackScroller.getIntrinsicContentHeight();
+ return Math.max(maxHeight, minKeyguardPanelBottom);
+ } else {
+ return maxHeight;
+ }
}
private int calculatePanelHeightQsExpanded() {
@@ -1652,7 +1610,8 @@
private void updateNotificationTranslucency() {
float alpha = 1f;
- if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp && !mHeadsUpManager.hasPinnedHeadsUp()) {
+ if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp &&
+ !mHeadsUpManager.hasPinnedHeadsUp()) {
alpha = getFadeoutAlpha();
}
mNotificationStackScroller.setAlpha(alpha);
@@ -1907,13 +1866,16 @@
if (view == null && mQsExpanded) {
return;
}
+ if (needsAnimation) {
+ mAnimateNextPositionUpdate = true;
+ }
ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
ExpandableNotificationRow firstRow = firstChildNotGone instanceof ExpandableNotificationRow
? (ExpandableNotificationRow) firstChildNotGone
: null;
if (firstRow != null
&& (view == firstRow || (firstRow.getNotificationParent() == firstRow))) {
- requestScrollerTopPaddingUpdate(false);
+ requestScrollerTopPaddingUpdate(false /* animate */);
}
requestPanelHeightUpdate();
}
@@ -1965,14 +1927,12 @@
@Override
public void onClick(View v) {
- if (v.getId() == R.id.expand_indicator) {
- onQsExpansionStarted();
- if (mQsExpanded) {
- flingSettings(0 /* vel */, false /* expand */, null, true /* isClick */);
- } else if (mQsExpansionEnabled) {
- mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
- flingSettings(0 /* vel */, true /* expand */, null, true /* isClick */);
- }
+ onQsExpansionStarted();
+ if (mQsExpanded) {
+ flingSettings(0 /* vel */, false /* expand */, null, true /* isClick */);
+ } else if (mQsExpansionEnabled) {
+ mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
+ flingSettings(0 /* vel */, true /* expand */, null, true /* isClick */);
}
}
@@ -2339,15 +2299,15 @@
p.setColor(Color.YELLOW);
canvas.drawLine(0, calculatePanelHeightShade(), getWidth(),
calculatePanelHeightShade(), p);
- p.setColor(Color.GRAY);
- canvas.drawLine(0, calculatePanelHeightShadeExpanded(), getWidth(),
- calculatePanelHeightShadeExpanded(), p);
p.setColor(Color.MAGENTA);
canvas.drawLine(0, calculateQsTopPadding(), getWidth(),
calculateQsTopPadding(), p);
p.setColor(Color.CYAN);
- canvas.drawLine(0, mNotificationStackScroller.getTopPadding(), getWidth(),
+ canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, getWidth(),
mNotificationStackScroller.getTopPadding(), p);
+ p.setColor(Color.GRAY);
+ canvas.drawLine(0, mClockPositionResult.clockY, getWidth(),
+ mClockPositionResult.clockY, p);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 304a499..347a4b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -828,7 +828,7 @@
}
@Override
- protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mStatusBar.onPanelLaidOut();
requestPanelHeightUpdate();
@@ -1088,13 +1088,10 @@
}
cancelPeek();
notifyExpandingStarted();
- startUnlockHintAnimationPhase1(new Runnable() {
- @Override
- public void run() {
- notifyExpandingFinished();
- onUnlockHintFinished();
- mHintAnimationRunning = false;
- }
+ startUnlockHintAnimationPhase1(() -> {
+ notifyExpandingFinished();
+ onUnlockHintFinished();
+ mHintAnimationRunning = false;
});
onUnlockHintStarted();
mHintAnimationRunning = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 0fd0a05..01582d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -332,6 +332,18 @@
if (cornerCutoutMargins != null) {
lp.leftMargin = Math.max(lp.leftMargin, cornerCutoutMargins.first);
lp.rightMargin = Math.max(lp.rightMargin, cornerCutoutMargins.second);
+
+ // If we're already inset enough (e.g. on the status bar side), we can have 0 margin
+ WindowInsets insets = getRootWindowInsets();
+ int leftInset = insets.getSystemWindowInsetLeft();
+ int rightInset = insets.getSystemWindowInsetRight();
+ if (lp.leftMargin <= leftInset) {
+ lp.leftMargin = 0;
+ }
+ if (lp.rightMargin <= rightInset) {
+ lp.rightMargin = 0;
+ }
+
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index d3790d4..ff5d0e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -215,16 +215,16 @@
int pos, touchDown, offset, trackSize;
if (mIsVertical) {
- exceededScrubTouchSlop = yDiff > QUICK_STEP_TOUCH_SLOP_PX && yDiff > xDiff;
- exceededSwipeUpTouchSlop = xDiff > QUICK_STEP_DRAG_SLOP_PX && xDiff > yDiff;
+ exceededScrubTouchSlop = yDiff > QUICK_SCRUB_TOUCH_SLOP_PX && yDiff > xDiff;
+ exceededSwipeUpTouchSlop = xDiff > QUICK_STEP_TOUCH_SLOP_PX && xDiff > yDiff;
exceededScrubDragSlop = yDiff > QUICK_SCRUB_DRAG_SLOP_PX && yDiff > xDiff;
pos = y;
touchDown = mTouchDownY;
offset = pos - mTrackRect.top;
trackSize = mTrackRect.height();
} else {
- exceededScrubTouchSlop = xDiff > QUICK_STEP_TOUCH_SLOP_PX && xDiff > yDiff;
- exceededSwipeUpTouchSlop = yDiff > QUICK_SCRUB_TOUCH_SLOP_PX && yDiff > xDiff;
+ exceededScrubTouchSlop = xDiff > QUICK_SCRUB_TOUCH_SLOP_PX && xDiff > yDiff;
+ exceededSwipeUpTouchSlop = yDiff > QUICK_STEP_TOUCH_SLOP_PX && yDiff > xDiff;
exceededScrubDragSlop = xDiff > QUICK_SCRUB_DRAG_SLOP_PX && xDiff > yDiff;
pos = x;
touchDown = mTouchDownX;
@@ -407,6 +407,7 @@
} catch (RemoteException e) {
Log.e(TAG, "Failed to send start of quick scrub.", e);
}
+ mOverviewEventSender.notifyQuickScrubStarted();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 235b1a9..1d64088 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -131,6 +131,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.MessagingGroup;
@@ -783,6 +784,12 @@
// into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
+ mZenController.addCallback(new ZenModeController.Callback() {
+ @Override
+ public void onZenChanged(int zen) {
+ updateEmptyShadeView();
+ }
+ });
mActivityLaunchAnimator = new ActivityLaunchAnimator(mStatusBarWindow,
this,
mNotificationPanel,
@@ -4682,7 +4689,8 @@
// tapping on a notification, editing QS or being dismissed by
// FLAG_DISMISS_KEYGUARD_ACTIVITY.
ScrimState state = mIsOccluded || mNotificationPanel.needsScrimming()
- || mStatusBarKeyguardViewManager.willDismissWithAction() ?
+ || mStatusBarKeyguardViewManager.willDismissWithAction()
+ || mStatusBarKeyguardViewManager.isFullscreenBouncer() ?
ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
mScrimController.transitionTo(state);
} else if (mLaunchCameraOnScreenTurningOn || isInLaunchTransition()) {
@@ -5121,8 +5129,13 @@
collapseOnMainThread();
}
+ final int count =
+ mEntryManager.getNotificationData().getActiveNotifications().size();
+ final int rank = mEntryManager.getNotificationData().getRank(notificationKey);
+ final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
+ rank, count, true);
try {
- mBarService.onNotificationClick(notificationKey);
+ mBarService.onNotificationClick(notificationKey, nv);
} catch (RemoteException ex) {
// system process is dead if we're here.
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index e207eb0..e63a2e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -165,14 +165,15 @@
// • The user quickly taps on the display and we show "swipe up to unlock."
// • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY
// • Full-screen user switcher is displayed.
- if (mOccluded || mNotificationPanelView.isUnlockHintRunning()
- || mBouncer.willDismissWithAction()
+ if (mNotificationPanelView.isUnlockHintRunning()) {
+ mBouncer.setExpansion(1);
+ } else if (mOccluded || mBouncer.willDismissWithAction()
|| mStatusBar.isFullScreenUserSwitcherState()) {
mBouncer.setExpansion(0);
- } else if (mShowing && mStatusBar.isKeyguardCurrentlySecure() && !mDozing) {
+ } else if (mShowing && !mDozing) {
mBouncer.setExpansion(expansion);
- if (expansion != 1 && tracking && !mBouncer.isShowing()
- && !mBouncer.isAnimatingAway()) {
+ if (expansion != 1 && tracking && mStatusBar.isKeyguardCurrentlySecure()
+ && !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
mBouncer.show(false /* resetSecuritySelection */, false /* animated */);
}
}
@@ -542,6 +543,10 @@
return mBouncer.isShowing();
}
+ public boolean isFullscreenBouncer() {
+ return mBouncer.isFullscreenBouncer();
+ }
+
private long getNavBarShowDelay() {
if (mStatusBar.isKeyguardFadingAway()) {
return mStatusBar.getKeyguardFadingAwayDelay();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index b693ebb..42e02d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -21,6 +21,7 @@
import com.android.systemui.statusbar.policy.BluetoothController.Callback;
import java.util.Collection;
+import java.util.List;
public interface BluetoothController extends CallbackController<Callback>, Dumpable {
boolean isBluetoothSupported();
@@ -30,7 +31,7 @@
boolean isBluetoothConnected();
boolean isBluetoothConnecting();
- String getLastDeviceName();
+ String getConnectedDeviceName();
void setBluetoothEnabled(boolean enabled);
Collection<CachedBluetoothDevice> getDevices();
void connect(CachedBluetoothDevice device);
@@ -39,7 +40,7 @@
int getMaxConnectionState(CachedBluetoothDevice device);
int getBondState(CachedBluetoothDevice device);
- CachedBluetoothDevice getLastDevice();
+ List<CachedBluetoothDevice> getConnectedDevices();
public interface Callback {
void onBluetoothStateChange(boolean enabled);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index cd17cfc..44e87ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -31,6 +31,7 @@
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.Dependency;
import java.io.FileDescriptor;
@@ -38,10 +39,11 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.WeakHashMap;
public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback,
- CachedBluetoothDevice.Callback {
+ CachedBluetoothDevice.Callback, LocalBluetoothProfileManager.ServiceListener {
private static final String TAG = "BluetoothController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -51,10 +53,10 @@
private final WeakHashMap<CachedBluetoothDevice, ActuallyCachedState> mCachedState =
new WeakHashMap<>();
private final Handler mBgHandler;
+ private final List<CachedBluetoothDevice> mConnectedDevices = new ArrayList<>();
private boolean mEnabled;
private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
- private CachedBluetoothDevice mLastDevice;
private final H mHandler = new H(Looper.getMainLooper());
private int mState;
@@ -65,6 +67,7 @@
if (mLocalBluetoothManager != null) {
mLocalBluetoothManager.getEventManager().setReceiverHandler(mBgHandler);
mLocalBluetoothManager.getEventManager().registerCallback(this);
+ mLocalBluetoothManager.getProfileManager().addServiceListener(this);
onBluetoothStateChanged(
mLocalBluetoothManager.getBluetoothAdapter().getBluetoothState());
}
@@ -88,11 +91,10 @@
}
pw.print(" mEnabled="); pw.println(mEnabled);
pw.print(" mConnectionState="); pw.println(stateToString(mConnectionState));
- pw.print(" mLastDevice="); pw.println(mLastDevice);
+ pw.print(" mConnectedDevices="); pw.println(mConnectedDevices);
pw.print(" mCallbacks.size="); pw.println(mHandler.mCallbacks.size());
pw.println(" Bluetooth Devices:");
- for (CachedBluetoothDevice device :
- mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) {
+ for (CachedBluetoothDevice device : getDevices()) {
pw.println(" " + getDeviceString(device));
}
}
@@ -121,8 +123,8 @@
}
@Override
- public CachedBluetoothDevice getLastDevice() {
- return mLastDevice;
+ public List<CachedBluetoothDevice> getConnectedDevices() {
+ return mConnectedDevices;
}
@Override
@@ -186,8 +188,11 @@
}
@Override
- public String getLastDeviceName() {
- return mLastDevice != null ? mLastDevice.getName() : null;
+ public String getConnectedDeviceName() {
+ if (mConnectedDevices.size() == 1) {
+ return mConnectedDevices.get(0).getName();
+ }
+ return null;
}
@Override
@@ -200,10 +205,7 @@
private void updateConnected() {
// Make sure our connection state is up to date.
int state = mLocalBluetoothManager.getBluetoothAdapter().getConnectionState();
- if (mLastDevice != null && !mLastDevice.isConnected()) {
- // Clear out last device if no longer connected.
- mLastDevice = null;
- }
+ mConnectedDevices.clear();
// If any of the devices are in a higher state than the adapter, move the adapter into
// that state.
for (CachedBluetoothDevice device : getDevices()) {
@@ -211,13 +213,12 @@
if (maxDeviceState > state) {
state = maxDeviceState;
}
- if (mLastDevice == null && device.isConnected()) {
- // Set as last connected device only if we don't have one.
- mLastDevice = device;
+ if (device.isConnected()) {
+ mConnectedDevices.add(device);
}
}
- if (mLastDevice == null && state == BluetoothAdapter.STATE_CONNECTED) {
+ if (mConnectedDevices.isEmpty() && state == BluetoothAdapter.STATE_CONNECTED) {
// If somehow we think we are connected, but have no connected devices, we aren't
// connected.
state = BluetoothAdapter.STATE_DISCONNECTED;
@@ -271,7 +272,6 @@
@Override
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
mCachedState.remove(cachedDevice);
- mLastDevice = cachedDevice;
updateConnected();
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@@ -293,6 +293,15 @@
return state;
}
+ @Override
+ public void onServiceConnected() {
+ updateConnected();
+ mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
+ }
+
+ @Override
+ public void onServiceDisconnected() {}
+
private static class ActuallyCachedState implements Runnable {
private final WeakReference<CachedBluetoothDevice> mDevice;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 1b02e15..22a48f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -269,13 +269,6 @@
}
if (mCode != 0) {
if (doIt) {
- // If there was a pending remote recents animation, then we need to
- // cancel the animation now before we handle the button itself. In the case
- // where we are going home and the recents animation has already started,
- // just cancel the recents animation, leaving the home stack in place
- boolean isHomeKey = mCode == KEYCODE_HOME;
- ActivityManagerWrapper.getInstance().cancelRecentsAnimation(!isHomeKey);
-
sendEvent(KeyEvent.ACTION_UP, 0);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index bc5a848..b1c0a96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -111,6 +111,7 @@
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.function.BiConsumer;
/**
@@ -163,6 +164,7 @@
private Paint mDebugPaint;
private int mContentHeight;
+ private int mIntrinsicContentHeight;
private int mCollapsedSize;
private int mPaddingBetweenElements;
private int mIncreasedPaddingBetweenElements;
@@ -537,15 +539,16 @@
canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
}
} else {
- float animProgress = Interpolators.FAST_OUT_SLOW_IN
- .getInterpolation(1f - mDarkAmount);
- float sidePaddingsProgress = Interpolators.FAST_OUT_SLOW_IN
- .getInterpolation((1f - mDarkAmount) * 2);
+ float inverseDark = 1 - mDarkAmount;
+ float yProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(inverseDark);
+ float xProgress = Interpolators.FAST_OUT_SLOW_IN
+ .getInterpolation(inverseDark * 2f);
+
mBackgroundAnimationRect.set(
- (int) MathUtils.lerp(darkLeft, lockScreenLeft, sidePaddingsProgress),
- (int) MathUtils.lerp(darkTop, lockScreenTop, animProgress),
- (int) MathUtils.lerp(darkRight, lockScreenRight, sidePaddingsProgress),
- (int) MathUtils.lerp(darkBottom, lockScreenBottom, animProgress));
+ (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
+ (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
+ (int) MathUtils.lerp(darkRight, lockScreenRight, xProgress),
+ (int) MathUtils.lerp(darkBottom, lockScreenBottom, yProgress));
if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
@@ -627,8 +630,12 @@
}
private void notifyHeightChangeListener(ExpandableView view) {
+ notifyHeightChangeListener(view, false /* needsAnimation */);
+ }
+
+ private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
if (mOnHeightChangedListener != null) {
- mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
+ mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
}
}
@@ -849,7 +856,7 @@
mNeedsAnimation = true;
}
requestChildrenUpdate();
- notifyHeightChangeListener(null);
+ notifyHeightChangeListener(null, animate);
}
}
@@ -914,6 +921,13 @@
updateClipping();
}
+ /**
+ * Return the height of the content ignoring the footer.
+ */
+ public int getIntrinsicContentHeight() {
+ return mIntrinsicContentHeight;
+ }
+
public void updateClipping() {
boolean animatingClipping = mDarkAmount > 0 && mDarkAmount < 1;
boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
@@ -2129,8 +2143,9 @@
for (int i = 0; i < getChildCount(); i++) {
ExpandableView expandableView = (ExpandableView) getChildAt(i);
+ boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
if (expandableView.getVisibility() != View.GONE
- && !expandableView.hasNoContentHeight()) {
+ && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
boolean limitReached = maxDisplayedNotifications != -1
&& numShownItems >= maxDisplayedNotifications;
boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark()
@@ -2178,6 +2193,7 @@
}
}
}
+ mIntrinsicContentHeight = height;
mContentHeight = height + mTopPadding + mBottomMargin;
updateScrollability();
clampScrollPosition();
@@ -3655,7 +3671,7 @@
updateContentHeight();
updateScrollPositionOnExpandInBottom(view);
clampScrollPosition();
- notifyHeightChangeListener(view);
+ notifyHeightChangeListener(view, needsAnimation);
ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
? (ExpandableNotificationRow) view
: null;
@@ -3969,6 +3985,7 @@
mShelf.fadeInTranslating();
}
}
+ updateAlgorithmHeightAndPadding();
updateBackgroundDimming();
updateAntiBurnInTranslation();
requestChildrenUpdate();
@@ -4039,14 +4056,21 @@
public void updateEmptyShadeView(boolean visible) {
int oldVisibility = mEmptyShadeView.willBeGone() ? GONE : mEmptyShadeView.getVisibility();
int newVisibility = visible ? VISIBLE : GONE;
- if (oldVisibility != newVisibility) {
+
+ boolean changedVisibility = oldVisibility != newVisibility;
+ if (changedVisibility || newVisibility != GONE) {
if (newVisibility != GONE) {
+ int oldText = mEmptyShadeView.getTextResource();
+ int newText;
if (mStatusBar.areNotificationsHidden()) {
- mEmptyShadeView.setText(R.string.dnd_suppressing_shade_text);
+ newText = R.string.dnd_suppressing_shade_text;
} else {
- mEmptyShadeView.setText(R.string.empty_shade_text);
+ newText = R.string.empty_shade_text;
}
- showFooterView(mEmptyShadeView);
+ if (changedVisibility || !Objects.equals(oldText, newText)) {
+ mEmptyShadeView.setText(newText);
+ showFooterView(mEmptyShadeView);
+ }
} else {
hideFooterView(mEmptyShadeView, true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index 41b094a..64abfe2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -16,27 +16,21 @@
package com.android.systemui.volume;
-import android.animation.ObjectAnimator;
-import android.annotation.SuppressLint;
+import android.annotation.Nullable;
import android.app.Dialog;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
import android.graphics.Color;
-import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
+import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.SystemClock;
-import android.provider.Settings.Global;
import android.util.Log;
-import android.util.Slog;
import android.util.SparseBooleanArray;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
@@ -45,16 +39,21 @@
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
+import androidx.car.widget.ListItem;
+import androidx.car.widget.ListItemAdapter;
+import androidx.car.widget.ListItemAdapter.BackgroundStyle;
+import androidx.car.widget.ListItemProvider.ListProvider;
+import androidx.car.widget.PagedListView;
+import androidx.car.widget.SeekbarListItem;
+
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
-import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.VolumeDialog;
@@ -73,42 +72,61 @@
private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
- private static final int UPDATE_ANIMATION_DURATION = 80;
private final Context mContext;
private final H mHandler = new H();
private final VolumeDialogController mController;
+ private final AudioManager mAudioManager;
private Window mWindow;
private CustomDialog mDialog;
private ViewGroup mDialogView;
- private ViewGroup mDialogRowsView;
+ private PagedListView mListView;
+ private ListItemAdapter mPagedListAdapter;
+ private final List<ListItem> mVolumeLineItems = new ArrayList<>();
private final List<VolumeRow> mRows = new ArrayList<>();
private ConfigurableTexts mConfigurableTexts;
private final SparseBooleanArray mDynamic = new SparseBooleanArray();
private final KeyguardManager mKeyguard;
private final Object mSafetyWarningLock = new Object();
- private final ColorStateList mActiveSliderTint;
- private final ColorStateList mInactiveSliderTint;
private boolean mShowing;
- private int mActiveStream;
- private int mPrevActiveStream;
private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE;
private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
private State mState;
private SafetyWarningDialog mSafetyWarning;
private boolean mHovering = false;
- private boolean mExpanded = false;
- private View mExpandBtn;
+ private boolean mExpanded;
+
+ private final View.OnClickListener mSupplementalIconListener = v -> {
+ mExpanded = !mExpanded;
+ if (mExpanded) {
+ for (VolumeRow row : mRows) {
+ // Adding the items which are not coming from default stream.
+ if (!row.defaultStream) {
+ addSeekbarListItem(row, null);
+ }
+ }
+ } else {
+ // Only keeping the default stream if it is not expended.
+ Iterator itr = mVolumeLineItems.iterator();
+ while (itr.hasNext()) {
+ SeekbarListItem item = (SeekbarListItem) itr.next();
+ VolumeRow row = findRow(item);
+ if (!row.defaultStream) {
+ itr.remove();
+ }
+ }
+ }
+ mPagedListAdapter.notifyDataSetChanged();
+ };
public CarVolumeDialogImpl(Context context) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mController = Dependency.get(VolumeDialogController.class);
mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
- mInactiveSliderTint = loadColorStateList(R.color.volume_slider_inactive);
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
}
public void init(int windowType, Callback callback) {
@@ -125,11 +143,14 @@
}
private void initDialog() {
+ mRows.clear();
+ mVolumeLineItems.clear();
mDialog = new CustomDialog(mContext);
mConfigurableTexts = new ConfigurableTexts(mContext);
mHovering = false;
mShowing = false;
+ mExpanded = false;
mWindow = mDialog.getWindow();
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
@@ -163,12 +184,7 @@
.setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
.start();
});
- mExpandBtn = mDialog.findViewById(R.id.expand);
- mExpandBtn.setOnClickListener(v -> {
- mExpanded = !mExpanded;
- updateRowsH(getActiveRow());
- });
- mDialogView = mDialog.findViewById(R.id.volume_dialog);
+ mDialogView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog);
mDialogView.setOnHoverListener((v, event) -> {
int action = event.getActionMasked();
mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
@@ -176,25 +192,20 @@
rescheduleTimeoutH();
return true;
});
+ mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
- mDialogRowsView = mDialog.findViewById(R.id.car_volume_dialog_rows);
+ // TODO: apply tint to the supplement icon.
+ addSeekbarListItem(addVolumeRow(AudioManager.STREAM_MUSIC, R.drawable.ic_volume_media,
+ R.drawable.car_ic_arrow_drop_up, true, true), mSupplementalIconListener);
+ addVolumeRow(AudioManager.STREAM_RING, R.drawable.ic_volume_ringer, 0,
+ true, false);
+ addVolumeRow(AudioManager.STREAM_ALARM, R.drawable.ic_volume_alarm, 0,
+ true, false);
- if (mRows.isEmpty()) {
- addRow(AudioManager.STREAM_MUSIC,
- R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
- addRow(AudioManager.STREAM_RING,
- R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, false);
- addRow(AudioManager.STREAM_ALARM,
- R.drawable.ic_volume_alarm, R.drawable.ic_volume_alarm_mute, true, false);
- } else {
- addExistingRows();
- }
-
- updateRowsH(getActiveRow());
- }
-
- private ColorStateList loadColorStateList(int colorResId) {
- return ColorStateList.valueOf(mContext.getColor(colorResId));
+ mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
+ BackgroundStyle.PANEL);
+ mListView.setAdapter(mPagedListAdapter);
+ mListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
}
public void setStreamImportant(int stream, boolean important) {
@@ -202,65 +213,52 @@
}
public void setAutomute(boolean automute) {
- if (mAutomute == automute) return;
+ if (mAutomute == automute) {
+ return;
+ }
mAutomute = automute;
mHandler.sendEmptyMessage(H.RECHECK_ALL);
}
public void setSilentMode(boolean silentMode) {
- if (mSilentMode == silentMode) return;
+ if (mSilentMode == silentMode) {
+ return;
+ }
mSilentMode = silentMode;
mHandler.sendEmptyMessage(H.RECHECK_ALL);
}
- private void addRow(int stream, int iconRes, int iconMuteRes, boolean important,
- boolean defaultStream) {
- addRow(stream, iconRes, iconMuteRes, important, defaultStream, false);
+ private VolumeRow addVolumeRow(int stream, int primaryActionIcon, int supplementalIcon,
+ boolean important, boolean defaultStream) {
+ VolumeRow volumeRow = new VolumeRow();
+ volumeRow.stream = stream;
+ volumeRow.primaryActionIcon = primaryActionIcon;
+ volumeRow.supplementalIcon = supplementalIcon;
+ volumeRow.important = important;
+ volumeRow.defaultStream = defaultStream;
+ volumeRow.listItem = null;
+ mRows.add(volumeRow);
+ return volumeRow;
}
- private void addRow(int stream, int iconRes, int iconMuteRes, boolean important,
- boolean defaultStream, boolean dynamic) {
- if (D.BUG) Slog.d(TAG, "Adding row for stream " + stream);
- VolumeRow row = new VolumeRow();
- initRow(row, stream, iconRes, iconMuteRes, important, defaultStream);
- mDialogRowsView.addView(row.view);
- mRows.add(row);
- }
-
- private void addExistingRows() {
- int N = mRows.size();
- for (int i = 0; i < N; i++) {
- final VolumeRow row = mRows.get(i);
- initRow(row, row.stream, row.iconRes, row.iconMuteRes, row.important,
- row.defaultStream);
- mDialogRowsView.addView(row.view);
- updateVolumeRowH(row);
+ private SeekbarListItem addSeekbarListItem(
+ VolumeRow volumeRow, @Nullable View.OnClickListener supplementalIconOnClickListener) {
+ int volumeMax = mAudioManager.getStreamMaxVolume(volumeRow.stream);
+ int currentVolume = mAudioManager.getStreamVolume(volumeRow.stream);
+ SeekbarListItem listItem =
+ new SeekbarListItem(mContext, volumeMax, currentVolume,
+ new VolumeSeekBarChangeListener(volumeRow), null);
+ listItem.setPrimaryActionIcon(volumeRow.primaryActionIcon);
+ if (volumeRow.supplementalIcon != 0) {
+ listItem.setSupplementalIcon(volumeRow.supplementalIcon, true, supplementalIconOnClickListener);
+ } else {
+ listItem.setSupplementalEmptyIcon(true);
}
- }
- private VolumeRow getActiveRow() {
- for (VolumeRow row : mRows) {
- if (row.stream == mActiveStream) {
- return row;
- }
- }
- return mRows.get(0);
- }
+ mVolumeLineItems.add(listItem);
+ volumeRow.listItem = listItem;
- private VolumeRow findRow(int stream) {
- for (VolumeRow row : mRows) {
- if (row.stream == stream) return row;
- }
- return null;
- }
-
- public void dump(PrintWriter writer) {
- writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
- writer.print(" mShowing: "); writer.println(mShowing);
- writer.print(" mActiveStream: "); writer.println(mActiveStream);
- writer.print(" mDynamic: "); writer.println(mDynamic);
- writer.print(" mAutomute: "); writer.println(mAutomute);
- writer.print(" mSilentMode: "); writer.println(mSilentMode);
+ return listItem;
}
private static int getImpliedLevel(SeekBar seekBar, int progress) {
@@ -271,25 +269,6 @@
return level;
}
- @SuppressLint("InflateParams")
- private void initRow(final VolumeRow row, final int stream, int iconRes, int iconMuteRes,
- boolean important, boolean defaultStream) {
- row.stream = stream;
- row.iconRes = iconRes;
- row.iconMuteRes = iconMuteRes;
- row.important = important;
- row.defaultStream = defaultStream;
- row.view = mDialog.getLayoutInflater().inflate(R.layout.car_volume_dialog_row, null);
- row.view.setId(row.stream);
- row.view.setTag(row);
- row.slider = row.view.findViewById(R.id.volume_row_slider);
- row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
- row.anim = null;
-
- row.icon = row.view.findViewById(R.id.volume_row_icon);
- row.icon.setImageResource(iconRes);
- }
-
public void show(int reason) {
mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget();
}
@@ -356,243 +335,87 @@
}
}
- private boolean shouldBeVisibleH(VolumeRow row) {
- if (mExpanded) {
- return true;
- }
- return row.defaultStream;
- }
-
- private void updateRowsH(final VolumeRow activeRow) {
- if (D.BUG) Log.d(TAG, "updateRowsH");
- if (!mShowing) {
- trimObsoleteH();
- }
- // apply changes to all rows
- for (final VolumeRow row : mRows) {
- final boolean isActive = row == activeRow;
- final boolean shouldBeVisible = shouldBeVisibleH(row);
- Util.setVisOrGone(row.view, shouldBeVisible);
- if (row.view.isShown()) {
- updateVolumeRowSliderTintH(row, isActive);
- }
- }
- }
-
private void trimObsoleteH() {
- if (D.BUG) Log.d(TAG, "trimObsoleteH");
+ int initialVolumeItemSize = mVolumeLineItems.size();
for (int i = mRows.size() - 1; i >= 0; i--) {
final VolumeRow row = mRows.get(i);
if (row.ss == null || !row.ss.dynamic) continue;
if (!mDynamic.get(row.stream)) {
mRows.remove(i);
- mDialogRowsView.removeView(row.view);
+ mVolumeLineItems.remove(row.listItem);
}
}
+
+ if (mVolumeLineItems.size() != initialVolumeItemSize) {
+ mPagedListAdapter.notifyDataSetChanged();
+ }
}
- protected void onStateChangedH(State state) {
+ private void onStateChangedH(State state) {
mState = state;
mDynamic.clear();
// add any new dynamic rows
for (int i = 0; i < state.states.size(); i++) {
final int stream = state.states.keyAt(i);
final StreamState ss = state.states.valueAt(i);
- if (!ss.dynamic) continue;
+ if (!ss.dynamic) {
+ continue;
+ }
mDynamic.put(stream, true);
if (findRow(stream) == null) {
- addRow(stream, R.drawable.ic_volume_remote, R.drawable.ic_volume_remote_mute, true,
- false, true);
+ VolumeRow row = addVolumeRow(stream, R.drawable.ic_volume_remote,
+ 0, true,false);
+ if (mExpanded) {
+ addSeekbarListItem(row, null);
+ }
}
}
- if (mActiveStream != state.activeStream) {
- mPrevActiveStream = mActiveStream;
- mActiveStream = state.activeStream;
- updateRowsH(getActiveRow());
- rescheduleTimeoutH();
- }
for (VolumeRow row : mRows) {
updateVolumeRowH(row);
}
-
}
private void updateVolumeRowH(VolumeRow row) {
if (D.BUG) Log.d(TAG, "updateVolumeRowH s=" + row.stream);
- if (mState == null) return;
- final StreamState ss = mState.states.get(row.stream);
- if (ss == null) return;
- row.ss = ss;
- if (ss.level > 0) {
- row.lastAudibleLevel = ss.level;
+ if (mState == null) {
+ return;
}
+ final StreamState ss = mState.states.get(row.stream);
+ if (ss == null) {
+ return;
+ }
+ row.ss = ss;
if (ss.level == row.requestedLevel) {
row.requestedLevel = -1;
}
- final boolean isRingStream = row.stream == AudioManager.STREAM_RING;
- final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM;
- final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM;
- final boolean isMusicStream = row.stream == AudioManager.STREAM_MUSIC;
- final boolean isRingVibrate = isRingStream
- && mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
- final boolean isRingSilent = isRingStream
- && mState.ringerModeInternal == AudioManager.RINGER_MODE_SILENT;
- final boolean isZenPriorityOnly = mState.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- final boolean isZenAlarms = mState.zenMode == Global.ZEN_MODE_ALARMS;
- final boolean isZenNone = mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
- final boolean zenMuted = isZenAlarms ? (isRingStream || isSystemStream)
- : isZenNone ? (isRingStream || isSystemStream || isAlarmStream || isMusicStream)
- : isZenPriorityOnly ? ((isAlarmStream && mState.disallowAlarms) ||
- (isMusicStream && mState.disallowMedia) ||
- (isRingStream && mState.disallowRinger) ||
- (isSystemStream && mState.disallowSystem))
- : false;
-
- // update slider max
- final int max = ss.levelMax * 100;
- if (max != row.slider.getMax()) {
- row.slider.setMax(max);
- }
-
- // update icon
- final boolean iconEnabled = (mAutomute || ss.muteSupported) && !zenMuted;
- row.icon.setEnabled(iconEnabled);
- row.icon.setAlpha(iconEnabled ? 1 : 0.5f);
- final int iconRes =
- isRingVibrate ? R.drawable.ic_volume_ringer_vibrate
- : isRingSilent || zenMuted ? row.iconMuteRes
- : ss.routedToBluetooth ?
- (ss.muted ? R.drawable.ic_volume_media_bt_mute
- : R.drawable.ic_volume_media_bt)
- : mAutomute && ss.level == 0 ? row.iconMuteRes
- : (ss.muted ? row.iconMuteRes : row.iconRes);
- row.icon.setImageResource(iconRes);
- row.iconState =
- iconRes == R.drawable.ic_volume_ringer_vibrate ? Events.ICON_STATE_VIBRATE
- : (iconRes == R.drawable.ic_volume_media_bt_mute || iconRes == row.iconMuteRes)
- ? Events.ICON_STATE_MUTE
- : (iconRes == R.drawable.ic_volume_media_bt || iconRes == row.iconRes)
- ? Events.ICON_STATE_UNMUTE
- : Events.ICON_STATE_UNKNOWN;
- if (iconEnabled) {
- if (isRingStream) {
- if (isRingVibrate) {
- row.icon.setContentDescription(mContext.getString(
- R.string.volume_stream_content_description_unmute,
- getStreamLabelH(ss)));
- } else {
- if (mController.hasVibrator()) {
- row.icon.setContentDescription(mContext.getString(
- R.string.volume_stream_content_description_vibrate,
- getStreamLabelH(ss)));
- } else {
- row.icon.setContentDescription(mContext.getString(
- R.string.volume_stream_content_description_mute,
- getStreamLabelH(ss)));
- }
- }
- } else {
- if (ss.muted || mAutomute && ss.level == 0) {
- row.icon.setContentDescription(mContext.getString(
- R.string.volume_stream_content_description_unmute,
- getStreamLabelH(ss)));
- } else {
- row.icon.setContentDescription(mContext.getString(
- R.string.volume_stream_content_description_mute,
- getStreamLabelH(ss)));
- }
- }
- } else {
- row.icon.setContentDescription(getStreamLabelH(ss));
- }
-
- // ensure tracking is disabled if zenMuted
- if (zenMuted) {
- row.tracking = false;
- }
-
- // update slider
- final boolean enableSlider = !zenMuted;
- final int vlevel = row.ss.muted && (!isRingStream && !zenMuted) ? 0
- : row.ss.level;
- updateVolumeRowSliderH(row, enableSlider, vlevel);
+ // TODO: update Seekbar progress and change the mute icon if necessary.
}
- private String getStreamLabelH(StreamState ss) {
- if (ss.remoteLabel != null) {
- return ss.remoteLabel;
- }
- try {
- return mContext.getResources().getString(ss.name);
- } catch (Resources.NotFoundException e) {
- Slog.e(TAG, "Can't find translation for stream " + ss);
- return "";
- }
- }
-
- private void updateVolumeRowSliderTintH(VolumeRow row, boolean isActive) {
- if (isActive) {
- row.slider.requestFocus();
- }
- final ColorStateList tint = isActive && row.slider.isEnabled() ? mActiveSliderTint
- : mInactiveSliderTint;
- if (tint == row.cachedSliderTint) return;
- row.cachedSliderTint = tint;
- row.slider.setProgressTintList(tint);
- row.slider.setThumbTintList(tint);
- }
-
- private void updateVolumeRowSliderH(VolumeRow row, boolean enable, int vlevel) {
- row.slider.setEnabled(enable);
- updateVolumeRowSliderTintH(row, row.stream == mActiveStream);
- if (row.tracking) {
- return; // don't update if user is sliding
- }
- final int progress = row.slider.getProgress();
- final int level = getImpliedLevel(row.slider, progress);
- final boolean rowVisible = row.view.getVisibility() == View.VISIBLE;
- final boolean inGracePeriod = (SystemClock.uptimeMillis() - row.userAttempt)
- < USER_ATTEMPT_GRACE_PERIOD;
- mHandler.removeMessages(H.RECHECK, row);
- if (mShowing && rowVisible && inGracePeriod) {
- if (D.BUG) Log.d(TAG, "inGracePeriod");
- mHandler.sendMessageAtTime(mHandler.obtainMessage(H.RECHECK, row),
- row.userAttempt + USER_ATTEMPT_GRACE_PERIOD);
- return; // don't update if visible and in grace period
- }
- if (vlevel == level) {
- if (mShowing && rowVisible) {
- return; // don't clamp if visible
+ private VolumeRow findRow(int stream) {
+ for (VolumeRow row : mRows) {
+ if (row.stream == stream) {
+ return row;
}
}
- final int newProgress = vlevel * 100;
- if (progress != newProgress) {
- if (mShowing && rowVisible) {
- // animate!
- if (row.anim != null && row.anim.isRunning()
- && row.animTargetProgress == newProgress) {
- return; // already animating to the target progress
- }
- // start/update animation
- if (row.anim == null) {
- row.anim = ObjectAnimator.ofInt(row.slider, "progress", progress, newProgress);
- row.anim.setInterpolator(new DecelerateInterpolator());
- } else {
- row.anim.cancel();
- row.anim.setIntValues(progress, newProgress);
- }
- row.animTargetProgress = newProgress;
- row.anim.setDuration(UPDATE_ANIMATION_DURATION);
- row.anim.start();
- } else {
- // update slider directly to clamped value
- if (row.anim != null) {
- row.anim.cancel();
- }
- row.slider.setProgress(newProgress, true);
+ return null;
+ }
+
+ private VolumeRow findRow(SeekbarListItem targetItem) {
+ for (VolumeRow row : mRows) {
+ if (row.listItem == targetItem) {
+ return row;
}
}
+ return null;
+ }
+
+ public void dump(PrintWriter writer) {
+ writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
+ writer.print(" mShowing: "); writer.println(mShowing);
+ writer.print(" mDynamic: "); writer.println(mDynamic);
+ writer.print(" mAutomute: "); writer.println(mAutomute);
+ writer.print(" mSilentMode: "); writer.println(mSilentMode);
}
private void recheckH(VolumeRow row) {
@@ -641,7 +464,7 @@
}
private final VolumeDialogController.Callbacks mControllerCallbackH
- = new VolumeDialogController.Callbacks() {
+ = new VolumeDialogController.Callbacks() {
@Override
public void onShowRequested(int reason) {
showH(reason);
@@ -763,18 +586,24 @@
private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
private final VolumeRow mRow;
- private VolumeSeekBarChangeListener(VolumeRow row) {
- mRow = row;
+ private VolumeSeekBarChangeListener(VolumeRow volumeRow) {
+ mRow = volumeRow;
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (mRow.ss == null) return;
- if (D.BUG) Log.d(TAG, AudioSystem.streamToString(mRow.stream)
- + " onProgressChanged " + progress + " fromUser=" + fromUser);
- if (!fromUser) return;
+ if (mRow.ss == null) {
+ return;
+ }
+ if (D.BUG) {
+ Log.d(TAG, AudioSystem.streamToString(mRow.stream)
+ + " onProgressChanged " + progress + " fromUser=" + fromUser);
+ }
+ if (!fromUser) {
+ return;
+ }
if (mRow.ss.levelMin > 0) {
- final int minProgress = mRow.ss.levelMin * 100;
+ final int minProgress = mRow.ss.levelMin;
if (progress < minProgress) {
seekBar.setProgress(minProgress);
progress = minProgress;
@@ -782,7 +611,6 @@
}
final int userLevel = getImpliedLevel(seekBar, progress);
if (mRow.ss.level != userLevel || mRow.ss.muted && userLevel > 0) {
- mRow.userAttempt = SystemClock.uptimeMillis();
if (mRow.requestedLevel != userLevel) {
mController.setStreamVolume(mRow.stream, userLevel);
mRow.requestedLevel = userLevel;
@@ -794,16 +622,17 @@
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
- if (D.BUG) Log.d(TAG, "onStartTrackingTouch"+ " " + mRow.stream);
+ if (D.BUG) {
+ Log.d(TAG, "onStartTrackingTouch"+ " " + mRow.stream);
+ }
mController.setActiveStream(mRow.stream);
- mRow.tracking = true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
- if (D.BUG) Log.d(TAG, "onStopTrackingTouch"+ " " + mRow.stream);
- mRow.tracking = false;
- mRow.userAttempt = SystemClock.uptimeMillis();
+ if (D.BUG) {
+ Log.d(TAG, "onStopTrackingTouch"+ " " + mRow.stream);
+ }
final int userLevel = getImpliedLevel(seekBar, seekBar.getProgress());
Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_DONE, mRow.stream, userLevel);
if (mRow.ss.level != userLevel) {
@@ -814,22 +643,13 @@
}
private static class VolumeRow {
- private View view;
- private ImageButton icon;
- private SeekBar slider;
private int stream;
private StreamState ss;
- private long userAttempt; // last user-driven slider change
- private boolean tracking; // tracking slider touch
- private int requestedLevel = -1; // pending user-requested level via progress changed
- private int iconRes;
- private int iconMuteRes;
private boolean important;
private boolean defaultStream;
- private ColorStateList cachedSliderTint;
- private int iconState; // from Events
- private ObjectAnimator anim; // slider progress animation for non-touch-related updates
- private int animTargetProgress;
- private int lastAudibleLevel = 1;
+ private int primaryActionIcon;
+ private int supplementalIcon;
+ private SeekbarListItem listItem;
+ private int requestedLevel = -1; // pending user-requested level via progress changed
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index c8fcfce..5c7ce59 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -411,7 +411,7 @@
public void initSettingsH() {
mSettingsView.setVisibility(
- mDeviceProvisionedController.isDeviceProvisioned() ? VISIBLE : GONE);
+ mDeviceProvisionedController.isCurrentUserSetup() ? VISIBLE : GONE);
mSettingsIcon.setOnClickListener(v -> {
Events.writeEvent(mContext, Events.EVENT_SETTINGS_CLICK);
Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
@@ -765,6 +765,11 @@
if (max != row.slider.getMax()) {
row.slider.setMax(max);
}
+ // update slider min
+ final int min = ss.levelMin * 100;
+ if (min != row.slider.getMin()) {
+ row.slider.setMin(min);
+ }
// update header text
Util.setText(row.header, getStreamLabelH(ss));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java
index 726810e..14fada5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java
@@ -93,7 +93,7 @@
waitForUiOffloadThread();
NotificationVisibility[] newlyVisibleKeys = {
- NotificationVisibility.obtain(mEntry.key, 0, true)
+ NotificationVisibility.obtain(mEntry.key, 0, 1, true)
};
NotificationVisibility[] noLongerVisibleKeys = {};
verify(mBarService).onNotificationVisibilityChanged(newlyVisibleKeys, noLongerVisibleKeys);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
index a153140..f0ca3ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.stack.AnimationProperties;
import com.android.systemui.statusbar.stack.ViewState;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -227,4 +228,13 @@
assertNotNull(animator);
assertTrue(animator.getListeners().contains(mFinishListener));
}
+
+ @Test
+ public void testIsAnimating() {
+ mAnimationFilter.reset();
+ mAnimationFilter.animate(mProperty.getProperty());
+ assertFalse(PropertyAnimator.isAnimating(mView, mProperty));
+ PropertyAnimator.startAnimation(mView, mProperty, 200f, mAnimationProperties);
+ assertTrue(PropertyAnimator.isAnimating(mView, mProperty));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index 4e7550b..54153a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -35,6 +35,7 @@
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -68,6 +69,8 @@
mMockAdapter = mock(LocalBluetoothAdapter.class);
when(mMockBluetoothManager.getBluetoothAdapter()).thenReturn(mMockAdapter);
when(mMockBluetoothManager.getEventManager()).thenReturn(mock(BluetoothEventManager.class));
+ when(mMockBluetoothManager.getProfileManager())
+ .thenReturn(mock(LocalBluetoothProfileManager.class));
mBluetoothControllerImpl = new BluetoothControllerImpl(mContext,
mTestableLooper.getLooper());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index d30e777..6591715 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -136,10 +136,9 @@
}
protected void setWifiEnabled(boolean enabled) {
- Intent i = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
- i.putExtra(WifiManager.EXTRA_WIFI_STATE,
+ when(mMockWm.getWifiState()).thenReturn(
enabled ? WifiManager.WIFI_STATE_ENABLED : WifiManager.WIFI_STATE_DISABLED);
- mNetworkController.onReceive(mContext, i);
+ mNetworkController.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
}
protected void setWifiState(boolean connected, String ssid) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
index dd2b581..eeb4209 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
@@ -141,6 +141,19 @@
}
@Test
+ public void updateEmptyView_noNotificationsToDndSuppressing() {
+ mStackScroller.setEmptyShadeView(mEmptyShadeView);
+ when(mEmptyShadeView.willBeGone()).thenReturn(true);
+ when(mBar.areNotificationsHidden()).thenReturn(false);
+ mStackScroller.updateEmptyShadeView(true);
+ verify(mEmptyShadeView).setText(R.string.empty_shade_text);
+
+ when(mBar.areNotificationsHidden()).thenReturn(true);
+ mStackScroller.updateEmptyShadeView(true);
+ verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
+ }
+
+ @Test
@UiThreadTest
public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
mStackScroller.setExpandedHeight(0f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
index 44c4983..cac6bf7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
@@ -21,6 +21,8 @@
import com.android.systemui.statusbar.policy.BluetoothController.Callback;
import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
public class FakeBluetoothController extends BaseLeakChecker<Callback> implements
BluetoothController {
@@ -55,7 +57,7 @@
}
@Override
- public String getLastDeviceName() {
+ public String getConnectedDeviceName() {
return null;
}
@@ -95,7 +97,7 @@
}
@Override
- public CachedBluetoothDevice getLastDevice() {
- return null;
+ public List<CachedBluetoothDevice> getConnectedDevices() {
+ return Collections.emptyList();
}
}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 93de08c..cd2e2a4 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -178,6 +178,16 @@
TEXT_SELECTION_INVOCATION_LINK = 2;
}
+ // Access method for hidden API events. Type of data tagged with
+ // FIELD_HIDDEN_API_ACCESS_METHOD.
+ // This must be kept in sync with enum AccessMethod in art/runtime/hidden_api.h
+ enum HiddenApiAccessMethod {
+ ACCESS_METHOD_NONE = 0; // never logged, included for completeness
+ ACCESS_METHOD_REFLECTION = 1;
+ ACCESS_METHOD_JNI = 2;
+ ACCESS_METHOD_LINKING = 3; // never logged, included for completeness
+ }
+
// Known visual elements: views or controls.
enum View {
// Unknown view
@@ -5664,6 +5674,29 @@
// OS: P
BLUETOOTH_FRAGMENT = 1390;
+ // Enclosing category for group of FIELD_HIDDEN_API_FOO events, logged when
+ // an app uses a hidden API.
+ ACTION_HIDDEN_API_ACCESSED = 1391;
+
+ // Tagged data for ACTION_HIDDEN_API_ACCESSED. The metod of the hidden API
+ // access; see enum HiddenApiAccessMethod
+ // OS: P
+ FIELD_HIDDEN_API_ACCESS_METHOD = 1392;
+
+ // Tagged data for ACTION_HIDDEN_API_ACCESSED. Indicates that access was
+ // denied to the API.
+ // OS: P
+ FIELD_HIDDEN_API_ACCESS_DENIED = 1393;
+
+ // Tagged data for ACTION_HIDDEN_API_ACCESSED. The signature of the hidden
+ // API that was accessed.
+ // OS: P
+ FIELD_HIDDEN_API_SIGNATURE = 1394;
+
+ // This value should never appear in log outputs - it is reserved for
+ // internal platform metrics use.
+ NOTIFICATION_SHADE_COUNT = 1395;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 06707da..e6b2a35 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -271,7 +271,8 @@
// Sanitize structure before it's sent to service.
final ComponentName componentNameFromApp = structure.getActivityComponent();
- if (!mComponentName.equals(componentNameFromApp)) {
+ if (componentNameFromApp == null || !mComponentName.getPackageName()
+ .equals(componentNameFromApp.getPackageName())) {
Slog.w(TAG, "Activity " + mComponentName + " forged different component on "
+ "AssistStructure: " + componentNameFromApp);
structure.setActivityComponent(mComponentName);
@@ -2150,11 +2151,8 @@
if (saveTriggerId != null) {
writeLog(MetricsEvent.AUTOFILL_EXPLICIT_SAVE_TRIGGER_DEFINITION);
}
- int flags = saveInfo.getFlags();
- if (mCompatMode) {
- flags |= SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE;
- }
- mSaveOnAllViewsInvisible = (flags & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
+ mSaveOnAllViewsInvisible =
+ (saveInfo.getFlags() & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
// We only need to track views if we want to save once they become invisible.
if (mSaveOnAllViewsInvisible) {
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 48bb409..2465ba2 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -72,11 +72,11 @@
# when notifications are expanded, or contracted
27511 notification_expansion (key|3),(user_action|1),(expanded|1),(lifespan|1),(freshness|1),(exposure|1)
# when a notification has been clicked
-27520 notification_clicked (key|3),(lifespan|1),(freshness|1),(exposure|1)
+27520 notification_clicked (key|3),(lifespan|1),(freshness|1),(exposure|1),(rank|1),(count|1)
# when a notification action button has been clicked
-27521 notification_action_clicked (key|3),(action_index|1),(lifespan|1),(freshness|1),(exposure|1)
+27521 notification_action_clicked (key|3),(action_index|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1),(count|1)
# when a notification has been canceled
-27530 notification_canceled (key|3),(reason|1),(lifespan|1),(freshness|1),(exposure|1),(listener|3)
+27530 notification_canceled (key|3),(reason|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1),(count|1),(listener|3)
# replaces 27510 with a row per notification
27531 notification_visibility (key|3),(visibile|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1)
# a notification emited noise, vibration, or light
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index f678eed..5b446ca 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -3950,6 +3950,7 @@
if (mSwitchingDialog != null) {
mSwitchingDialog.dismiss();
mSwitchingDialog = null;
+ mSwitchingDialogTitleView = null;
}
updateSystemUiLocked(mCurToken, mImeWindowVis, mBackDisposition);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 379658f..99e0459 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2667,9 +2667,17 @@
public void mkdirs(String callingPkg, String appPath) {
final int userId = UserHandle.getUserId(Binder.getCallingUid());
final UserEnvironment userEnv = new UserEnvironment(userId);
+ final String propertyName = "sys.user." + userId + ".ce_available";
// Ignore requests to create directories while storage is locked
- if (!isUserKeyUnlocked(userId)) return;
+ if (!isUserKeyUnlocked(userId)) {
+ throw new IllegalStateException("Failed to prepare " + appPath);
+ }
+
+ // Ignore requests to create directories if CE storage is not available
+ if (!SystemProperties.getBoolean(propertyName, false)) {
+ throw new IllegalStateException("Failed to prepare " + appPath);
+ }
// Validate that reported package name belongs to caller
final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 83fe976..60d11d7 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1426,6 +1426,31 @@
}
}
+ public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) {
+ if (!checkNotifyPermission("notifyOemHookRawEventForSubscriber")) {
+ return;
+ }
+
+ synchronized (mRecords) {
+ for (Record r : mRecords) {
+ if (VDBG) {
+ log("notifyOemHookRawEventForSubscriber: r=" + r + " subId=" + subId);
+ }
+ if ((r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT)) &&
+ ((r.subId == subId) ||
+ (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID))) {
+ try {
+ r.callback.onOemHookRawEvent(rawData);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -1693,6 +1718,11 @@
android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
}
+ if ((events & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
+ }
+
return true;
}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 66c2b07..83d2bf7 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -286,23 +286,28 @@
filter.addAction(Intent.ACTION_SCREEN_OFF);
context.registerReceiver(mIntentReceiver, filter);
- long[] clickEffectTimings = getLongIntArray(context.getResources(),
+ VibrationEffect clickEffect = createEffectFromResource(
com.android.internal.R.array.config_virtualKeyVibePattern);
- VibrationEffect clickEffect = createEffect(clickEffectTimings);
VibrationEffect doubleClickEffect = VibrationEffect.createWaveform(
DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS, -1 /*repeatIndex*/);
- long[] tickEffectTimings = getLongIntArray(context.getResources(),
+ VibrationEffect heavyClickEffect = createEffectFromResource(
+ com.android.internal.R.array.config_longPressVibePattern);
+ VibrationEffect tickEffect = createEffectFromResource(
com.android.internal.R.array.config_clockTickVibePattern);
- VibrationEffect tickEffect = createEffect(tickEffectTimings);
mFallbackEffects = new SparseArray<VibrationEffect>();
mFallbackEffects.put(VibrationEffect.EFFECT_CLICK, clickEffect);
mFallbackEffects.put(VibrationEffect.EFFECT_DOUBLE_CLICK, doubleClickEffect);
mFallbackEffects.put(VibrationEffect.EFFECT_TICK, tickEffect);
- mFallbackEffects.put(VibrationEffect.EFFECT_HEAVY_CLICK, clickEffect);
+ mFallbackEffects.put(VibrationEffect.EFFECT_HEAVY_CLICK, heavyClickEffect);
}
- private static VibrationEffect createEffect(long[] timings) {
+ private VibrationEffect createEffectFromResource(int resId) {
+ long[] timings = getLongIntArray(mContext.getResources(), resId);
+ return createEffectFromTimings(timings);
+ }
+
+ private static VibrationEffect createEffectFromTimings(long[] timings) {
if (timings == null || timings.length == 0) {
return null;
} else if (timings.length == 1) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index c8e0a5e..2e258c1 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -96,6 +96,7 @@
"android.hardware.camera.provider@2.4::ICameraProvider",
"android.hardware.graphics.composer@2.1::IComposer",
"android.hardware.media.omx@1.0::IOmx",
+ "android.hardware.media.omx@1.0::IOmxStore",
"android.hardware.sensors@1.0::ISensors",
"android.hardware.vr@1.0::IVr"
);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 8d2e3a2..b2797f9 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -58,6 +58,8 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.RegisteredServicesCacheListener;
import android.content.pm.ResolveInfo;
@@ -4737,9 +4739,11 @@
}
ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
int targetUid = targetActivityInfo.applicationInfo.uid;
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
if (!isExportedSystemActivity(targetActivityInfo)
- && (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid,
- targetUid))) {
+ && !pmi.hasSignatureCapability(
+ targetUid, authUid,
+ PackageParser.SigningDetails.CertCapabilities.AUTH)) {
String pkgName = targetActivityInfo.packageName;
String activityName = targetActivityInfo.name;
String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
@@ -5476,15 +5480,17 @@
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- // Check for signature match with Authenticator.
+ // Check for signature match with Authenticator.LocalServices.getService(PackageManagerInternal.class);
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo
: serviceInfos) {
if (accountType.equals(serviceInfo.type.type)) {
if (serviceInfo.uid == callingUid) {
return SIGNATURE_CHECK_UID_MATCH;
}
- final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
- if (sigChk == PackageManager.SIGNATURE_MATCH) {
+ if (pmi.hasSignatureCapability(
+ serviceInfo.uid, callingUid,
+ PackageParser.SigningDetails.CertCapabilities.AUTH)) {
return SIGNATURE_CHECK_MATCH;
}
}
@@ -5520,10 +5526,13 @@
} finally {
Binder.restoreCallingIdentity(identityToken);
}
+
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
serviceInfos) {
- if (isOtherwisePermitted || (mPackageManager.checkSignatures(serviceInfo.uid,
- callingUid) == PackageManager.SIGNATURE_MATCH)) {
+ if (isOtherwisePermitted || pmi.hasSignatureCapability(
+ serviceInfo.uid, callingUid,
+ PackageParser.SigningDetails.CertCapabilities.AUTH)) {
managedAccountTypes.add(serviceInfo.type.type);
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b1cb2fe..228171f 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -24,15 +24,19 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.function.Predicate;
import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.ServiceStartArgs;
+import android.content.ComponentName.WithComponentName;
import android.content.IIntentSender;
import android.content.IntentSender;
import android.content.pm.ParceledListSlice;
@@ -55,6 +59,8 @@
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.TransferPipe;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.server.AppStateTracker;
import com.android.server.LocalServices;
@@ -793,7 +799,7 @@
// Asked to only stop if done with all work. Note that
// to avoid leaks, we will take this as dropping all
// start items up to and including this one.
- ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
+ ServiceRecord.StartItem si = r.findDeliveredStart(startId, false, false);
if (si != null) {
while (r.deliveredStarts.size() > 0) {
ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
@@ -2869,14 +2875,14 @@
case Service.START_STICKY_COMPATIBILITY:
case Service.START_STICKY: {
// We are done with the associated start arguments.
- r.findDeliveredStart(startId, true);
+ r.findDeliveredStart(startId, false, true);
// Don't stop if killed.
r.stopIfKilled = false;
break;
}
case Service.START_NOT_STICKY: {
// We are done with the associated start arguments.
- r.findDeliveredStart(startId, true);
+ r.findDeliveredStart(startId, false, true);
if (r.getLastStartId() == startId) {
// There is no more work, and this service
// doesn't want to hang around if killed.
@@ -2888,7 +2894,7 @@
// We'll keep this item until they explicitly
// call stop for it, but keep track of the fact
// that it was delivered.
- ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
+ ServiceRecord.StartItem si = r.findDeliveredStart(startId, false, false);
if (si != null) {
si.deliveryCount = 0;
si.doneExecutingCount++;
@@ -2900,7 +2906,7 @@
case Service.START_TASK_REMOVED_COMPLETE: {
// Special processing for onTaskRemoved(). Don't
// impact normal onStartCommand() processing.
- r.findDeliveredStart(startId, true);
+ r.findDeliveredStart(startId, true, true);
break;
}
default:
@@ -3187,7 +3193,7 @@
stopServiceLocked(sr);
} else {
sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
- sr.makeNextStartId(), baseIntent, null, 0));
+ sr.getLastStartId(), baseIntent, null, 0));
if (sr.app != null && sr.app.thread != null) {
// We always run in the foreground, since this is called as
// part of the "remove task" UI operation.
@@ -4063,57 +4069,26 @@
* - the first arg isn't the flattened component name of an existing service:
* dump all services whose component contains the first arg as a substring
*/
- protected boolean dumpService(FileDescriptor fd, PrintWriter pw, String name, String[] args,
- int opti, boolean dumpAll) {
- ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+ protected boolean dumpService(FileDescriptor fd, PrintWriter pw, final String name,
+ String[] args, int opti, boolean dumpAll) {
+ final ArrayList<ServiceRecord> services = new ArrayList<>();
+
+ final Predicate<ServiceRecord> filter = DumpUtils.filterRecord(name);
synchronized (mAm) {
int[] users = mAm.mUserController.getUsers();
- if ("all".equals(name)) {
- for (int user : users) {
- ServiceMap smap = mServiceMap.get(user);
- if (smap == null) {
- continue;
- }
- ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
- for (int i=0; i<alls.size(); i++) {
- ServiceRecord r1 = alls.valueAt(i);
- services.add(r1);
- }
- }
- } else {
- ComponentName componentName = name != null
- ? ComponentName.unflattenFromString(name) : null;
- int objectId = 0;
- if (componentName == null) {
- // Not a '/' separated full component name; maybe an object ID?
- try {
- objectId = Integer.parseInt(name, 16);
- name = null;
- componentName = null;
- } catch (RuntimeException e) {
- }
- }
- for (int user : users) {
- ServiceMap smap = mServiceMap.get(user);
- if (smap == null) {
- continue;
- }
- ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
- for (int i=0; i<alls.size(); i++) {
- ServiceRecord r1 = alls.valueAt(i);
- if (componentName != null) {
- if (r1.name.equals(componentName)) {
- services.add(r1);
- }
- } else if (name != null) {
- if (r1.name.flattenToString().contains(name)) {
- services.add(r1);
- }
- } else if (System.identityHashCode(r1) == objectId) {
- services.add(r1);
- }
+ for (int user : users) {
+ ServiceMap smap = mServiceMap.get(user);
+ if (smap == null) {
+ continue;
+ }
+ ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
+ for (int i=0; i<alls.size(); i++) {
+ ServiceRecord r1 = alls.valueAt(i);
+
+ if (filter.test(r1)) {
+ services.add(r1);
}
}
}
@@ -4123,6 +4098,9 @@
return false;
}
+ // Sort by component name.
+ services.sort(Comparator.comparing(WithComponentName::getComponentName));
+
boolean needSep = false;
for (int i=0; i<services.size(); i++) {
if (needSep) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9d2cfe3..6718d95 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -95,6 +95,7 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE;
import static android.os.Process.THREAD_GROUP_DEFAULT;
+import static android.os.Process.THREAD_GROUP_RESTRICTED;
import static android.os.Process.THREAD_GROUP_TOP_APP;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
@@ -2945,7 +2946,11 @@
? Collections.emptyList()
: Arrays.asList(exemptions.split(","));
}
- zygoteProcess.setApiBlacklistExemptions(mExemptions);
+ if (!zygoteProcess.setApiBlacklistExemptions(mExemptions)) {
+ Slog.e(TAG, "Failed to set API blacklist exemptions!");
+ // leave mExemptionsStr as is, so we don't try to send the same list again.
+ mExemptions = Collections.emptyList();
+ }
}
int logSampleRate = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, -1);
@@ -3161,6 +3166,8 @@
// bind background thread to little cores
// this is expected to fail inside of framework tests because apps can't touch cpusets directly
+ // make sure we've already adjusted system_server's internal view of itself first
+ updateOomAdjLocked();
try {
Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(),
Process.THREAD_GROUP_BG_NONINTERACTIVE);
@@ -5280,22 +5287,7 @@
final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
mActivityStartController, mWindowManager, mUserController, callingPid);
anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
- recentsUid);
- }
-
- // If provided, kick off the request for the assist data in the background. Do not hold
- // the AM lock as this will just proxy directly to the assist data receiver provided.
- if (assistDataReceiver != null) {
- final AppOpsManager appOpsManager = (AppOpsManager)
- mContext.getSystemService(Context.APP_OPS_SERVICE);
- final AssistDataReceiverProxy proxy = new AssistDataReceiverProxy(
- assistDataReceiver, recentsPackage);
- final AssistDataRequester requester = new AssistDataRequester(mContext, this,
- mWindowManager, appOpsManager, proxy, this, OP_ASSIST_STRUCTURE, OP_NONE);
- requester.requestAssistData(topVisibleActivities,
- true /* fetchData */, false /* fetchScreenshots */,
- true /* allowFetchData */, false /* allowFetchScreenshots */,
- recentsUid, recentsPackage);
+ recentsUid, assistDataReceiver);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -13206,6 +13198,7 @@
mHandler.obtainMessage(DISPATCH_SCREEN_AWAKE_MSG, isAwake ? 1 : 0, 0)
.sendToTarget();
}
+ updateOomAdjLocked();
}
}
@@ -18195,6 +18188,9 @@
case ProcessList.SCHED_GROUP_TOP_APP:
schedGroup = 'T';
break;
+ case ProcessList.SCHED_GROUP_RESTRICTED:
+ schedGroup = 'R';
+ break;
default:
schedGroup = '?';
break;
@@ -23020,8 +23016,8 @@
app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
app.adjType = "pers-top-activity";
} else if (app.hasTopUi) {
+ // sched group/proc state adjustment is below
app.systemNoUi = false;
- app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
app.adjType = "pers-top-ui";
} else if (activitiesSize > 0) {
for (int j = 0; j < activitiesSize; j++) {
@@ -23032,7 +23028,15 @@
}
}
if (!app.systemNoUi) {
- app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+ if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
+ // screen on, promote UI
+ app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+ app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+ } else {
+ // screen off, restrict UI scheduling
+ app.curProcState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ app.curSchedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
+ }
}
return (app.curAdj=app.maxAdj);
}
@@ -23515,8 +23519,14 @@
int newAdj;
if ((cr.flags&(Context.BIND_ABOVE_CLIENT
|Context.BIND_IMPORTANT)) != 0) {
- newAdj = clientAdj >= ProcessList.PERSISTENT_SERVICE_ADJ
- ? clientAdj : ProcessList.PERSISTENT_SERVICE_ADJ;
+ if (clientAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) {
+ newAdj = clientAdj;
+ } else {
+ // make this service persistent
+ newAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ procState = ActivityManager.PROCESS_STATE_PERSISTENT;
+ }
} else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
&& clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
&& adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -23884,6 +23894,15 @@
}
}
+ // Put bound foreground services in a special sched group for additional
+ // restrictions on screen off
+ if (procState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE &&
+ mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
+ if (schedGroup > ProcessList.SCHED_GROUP_RESTRICTED) {
+ schedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
+ }
+ }
+
// Do final modification to adj. Everything we do between here and applying
// the final setAdj must be done in this function, because we will also use
// it when computing the final cached adj later. Note that we don't need to
@@ -24306,6 +24325,9 @@
case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
processGroup = THREAD_GROUP_TOP_APP;
break;
+ case ProcessList.SCHED_GROUP_RESTRICTED:
+ processGroup = THREAD_GROUP_RESTRICTED;
+ break;
default:
processGroup = THREAD_GROUP_DEFAULT;
break;
@@ -26611,6 +26633,11 @@
}
@Override
+ public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
+ ActivityManagerService.this.cancelRecentsAnimation(restoreHomeStackPosition);
+ }
+
+ @Override
public boolean isUidActive(int uid) {
synchronized (ActivityManagerService.this) {
final UidRecord uidRec = mActiveUids.get(uid);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e54e645..e20356f 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -91,6 +91,7 @@
import static com.android.server.am.ActivityStackProto.TASKS;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
@@ -3555,6 +3556,8 @@
int taskNdx = mTaskHistory.indexOf(finishedTask);
final TaskRecord task = finishedTask;
int activityNdx = task.mActivities.indexOf(r);
+ mWindowManager.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* TODO */,
+ 0, true /* forceOverride */);
finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
finishedTask = task;
// Also terminate any activities below it that aren't yet
@@ -4983,6 +4986,8 @@
+ r.intent.getComponent().flattenToShortString());
// Force the destroy to skip right to removal.
r.app = null;
+ mWindowManager.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE,
+ false /* TODO */, 0, true /* forceOverride */);
finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false,
"handleAppCrashedLocked");
}
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index 7b9b659..cd39bcd 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -32,7 +32,7 @@
import java.util.ArrayList;
import java.util.HashMap;
-final class ContentProviderRecord {
+final class ContentProviderRecord implements ComponentName.WithComponentName {
final ActivityManagerService service;
public final ProviderInfo info;
final int uid;
@@ -260,4 +260,8 @@
}
}
}
+
+ public ComponentName getComponentName() {
+ return name;
+ }
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index bf7aef9..784d62e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -128,13 +128,15 @@
// Activity manager's version of Process.THREAD_GROUP_BG_NONINTERACTIVE
static final int SCHED_GROUP_BACKGROUND = 0;
+ // Activity manager's version of Process.THREAD_GROUP_RESTRICTED
+ static final int SCHED_GROUP_RESTRICTED = 1;
// Activity manager's version of Process.THREAD_GROUP_DEFAULT
- static final int SCHED_GROUP_DEFAULT = 1;
+ static final int SCHED_GROUP_DEFAULT = 2;
// Activity manager's version of Process.THREAD_GROUP_TOP_APP
- static final int SCHED_GROUP_TOP_APP = 2;
+ static final int SCHED_GROUP_TOP_APP = 3;
// Activity manager's version of Process.THREAD_GROUP_TOP_APP
// Disambiguate between actual top app and processes bound to the top app
- static final int SCHED_GROUP_TOP_APP_BOUND = 3;
+ static final int SCHED_GROUP_TOP_APP_BOUND = 4;
// The minimum number of cached apps we want to be able to keep around,
// without empty apps being able to push them out of memory.
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index 8a905f8..2f52002 100644
--- a/services/core/java/com/android/server/am/ProviderMap.java
+++ b/services/core/java/com/android/server/am/ProviderMap.java
@@ -17,22 +17,28 @@
package com.android.server.am;
import android.content.ComponentName;
+import android.content.ComponentName.WithComponentName;
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.os.TransferPipe;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DumpUtils;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import java.util.function.Predicate;
/**
* Keeps track of content providers by authority (name) and class. It separates the mapping by
@@ -325,7 +331,9 @@
private ArrayList<ContentProviderRecord> getProvidersForName(String name) {
ArrayList<ContentProviderRecord> allProviders = new ArrayList<ContentProviderRecord>();
- ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
+ final ArrayList<ContentProviderRecord> ret = new ArrayList<>();
+
+ final Predicate<ContentProviderRecord> filter = DumpUtils.filterRecord(name);
synchronized (mAm) {
allProviders.addAll(mSingletonByClass.values());
@@ -333,39 +341,11 @@
allProviders.addAll(mProvidersByClassPerUser.valueAt(i).values());
}
- if ("all".equals(name)) {
- providers.addAll(allProviders);
- } else {
- ComponentName componentName = name != null
- ? ComponentName.unflattenFromString(name) : null;
- int objectId = 0;
- if (componentName == null) {
- // Not a '/' separated full component name; maybe an object ID?
- try {
- objectId = Integer.parseInt(name, 16);
- name = null;
- componentName = null;
- } catch (RuntimeException e) {
- }
- }
-
- for (int i=0; i<allProviders.size(); i++) {
- ContentProviderRecord r1 = allProviders.get(i);
- if (componentName != null) {
- if (r1.name.equals(componentName)) {
- providers.add(r1);
- }
- } else if (name != null) {
- if (r1.name.flattenToString().contains(name)) {
- providers.add(r1);
- }
- } else if (System.identityHashCode(r1) == objectId) {
- providers.add(r1);
- }
- }
- }
+ CollectionUtils.addIf(allProviders, ret, filter);
}
- return providers;
+ // Sort by component name.
+ ret.sort(Comparator.comparing(WithComponentName::getComponentName));
+ return ret;
}
protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index 06b5e20..f2ed6a2 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -17,6 +17,8 @@
package com.android.server.am;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
+import static android.app.AppOpsManager.OP_NONE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -30,7 +32,10 @@
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
import android.app.ActivityOptions;
+import android.app.AppOpsManager;
+import android.app.IAssistDataReceiver;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.os.RemoteException;
import android.os.Trace;
@@ -58,6 +63,7 @@
private final int mCallingPid;
private int mTargetActivityType;
+ private AssistDataRequester mAssistDataRequester;
// The stack to restore the target stack behind when the animation is finished
private ActivityStack mRestoreTargetBehindStack;
@@ -75,8 +81,10 @@
}
void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
- ComponentName recentsComponent, int recentsUid) {
- if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + intent);
+ ComponentName recentsComponent, int recentsUid,
+ IAssistDataReceiver assistDataReceiver) {
+ if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + intent
+ + " assistDataReceiver=" + assistDataReceiver);
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
if (!mWindowManager.canStartRecentsAnimation()) {
@@ -120,6 +128,20 @@
mWindowManager.deferSurfaceLayout();
try {
+ // Kick off the assist data request in the background before showing the target activity
+ if (assistDataReceiver != null) {
+ final AppOpsManager appOpsManager = (AppOpsManager)
+ mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
+ final AssistDataReceiverProxy proxy = new AssistDataReceiverProxy(
+ assistDataReceiver, recentsComponent.getPackageName());
+ mAssistDataRequester = new AssistDataRequester(mService.mContext, mService,
+ mWindowManager, appOpsManager, proxy, this, OP_ASSIST_STRUCTURE, OP_NONE);
+ mAssistDataRequester.requestAssistData(mStackSupervisor.getTopVisibleActivities(),
+ true /* fetchData */, false /* fetchScreenshots */,
+ true /* allowFetchData */, false /* allowFetchScreenshots */,
+ recentsUid, recentsComponent.getPackageName());
+ }
+
final ActivityDisplay display;
if (hasExistingActivity) {
// Move the recents activity into place for the animation if it is not top most
@@ -184,6 +206,13 @@
if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller="
+ mWindowManager.getRecentsAnimationController()
+ " reorderMode=" + reorderMode);
+
+ // Cancel the associated assistant data request
+ if (mAssistDataRequester != null) {
+ mAssistDataRequester.cancel();
+ mAssistDataRequester = null;
+ }
+
if (mWindowManager.getRecentsAnimationController() == null) return;
// Just to be sure end the launch hint in case the target activity was never launched.
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index f296c60..32887e4 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -56,7 +56,7 @@
/**
* A running application service.
*/
-final class ServiceRecord extends Binder {
+final class ServiceRecord extends Binder implements ComponentName.WithComponentName {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ServiceRecord" : TAG_AM;
// Maximum number of delivery attempts before giving up.
@@ -550,11 +550,11 @@
restartTime = 0;
}
- public StartItem findDeliveredStart(int id, boolean remove) {
+ public StartItem findDeliveredStart(int id, boolean taskRemoved, boolean remove) {
final int N = deliveredStarts.size();
for (int i=0; i<N; i++) {
StartItem si = deliveredStarts.get(i);
- if (si.id == id) {
+ if (si.id == id && si.taskRemoved == taskRemoved) {
if (remove) deliveredStarts.remove(i);
return si;
}
@@ -757,4 +757,8 @@
.append(' ').append(shortName).append('}');
return stringName = sb.toString();
}
+
+ public ComponentName getComponentName() {
+ return name;
+ }
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 060d4c8..8ad8256 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -400,6 +400,10 @@
// Call onBeforeUnlockUser on a worker thread that allows disk I/O
FgThread.getHandler().post(() -> {
+ if (!StorageManager.isUserKeyUnlocked(userId)) {
+ Slog.w(TAG, "User key got locked unexpectedly, leaving user locked.");
+ return;
+ }
mInjector.getUserManager().onBeforeUnlockUser(userId);
synchronized (mLock) {
// Do not proceed if unexpected state
@@ -714,14 +718,11 @@
void finishUserStopped(UserState uss) {
final int userId = uss.mHandle.getIdentifier();
- boolean stopped;
+ final boolean stopped;
ArrayList<IStopUserCallback> callbacks;
- boolean forceStopUser = false;
synchronized (mLock) {
callbacks = new ArrayList<>(uss.mStopCallbacks);
- if (mStartedUsers.get(userId) != uss) {
- stopped = false;
- } else if (uss.state != UserState.STATE_SHUTDOWN) {
+ if (mStartedUsers.get(userId) != uss || uss.state != UserState.STATE_SHUTDOWN) {
stopped = false;
} else {
stopped = true;
@@ -729,10 +730,10 @@
mStartedUsers.remove(userId);
mUserLru.remove(Integer.valueOf(userId));
updateStartedUserArrayLU();
- forceStopUser = true;
}
}
- if (forceStopUser) {
+
+ if (stopped) {
mInjector.getUserManagerInternal().removeUserState(userId);
mInjector.activityManagerOnUserStopped(userId);
// Clean up all state and processes associated with the user.
@@ -755,12 +756,23 @@
if (getUserInfo(userId).isEphemeral()) {
mInjector.getUserManager().removeUserEvenWhenDisallowed(userId);
}
- // Evict the user's credential encryption key.
- try {
- getStorageManager().lockUserKey(userId);
- } catch (RemoteException re) {
- throw re.rethrowAsRuntimeException();
- }
+
+ // Evict the user's credential encryption key. Performed on FgThread to make it
+ // serialized with call to UserManagerService.onBeforeUnlockUser in finishUserUnlocking
+ // to prevent data corruption.
+ FgThread.getHandler().post(() -> {
+ synchronized (mLock) {
+ if (mStartedUsers.get(userId) != null) {
+ Slog.w(TAG, "User was restarted, skipping key eviction");
+ return;
+ }
+ }
+ try {
+ getStorageManager().lockUserKey(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowAsRuntimeException();
+ }
+ });
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 8212463..cc3a489 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5971,6 +5971,8 @@
mConnectedDevices.remove(deviceKey);
return true;
}
+ Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey + ", deviceSpec="
+ + deviceSpec + ", connect=" + connect);
}
return false;
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 3f39f45..f74ac47 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -25,6 +25,7 @@
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ContentProvider;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.IClipboard;
import android.content.IOnPrimaryClipChangedListener;
@@ -490,17 +491,18 @@
}
}
- private final void checkUriOwnerLocked(Uri uri, int uid) {
- if (!"content".equals(uri.getScheme())) {
- return;
- }
- long ident = Binder.clearCallingIdentity();
+ private final void checkUriOwnerLocked(Uri uri, int sourceUid) {
+ if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
+
+ final long ident = Binder.clearCallingIdentity();
try {
- // This will throw SecurityException for us.
- mAm.checkGrantUriPermission(uid, null, ContentProvider.getUriWithoutUserId(uri),
+ // This will throw SecurityException if caller can't grant
+ mAm.checkGrantUriPermission(sourceUid, null,
+ ContentProvider.getUriWithoutUserId(uri),
Intent.FLAG_GRANT_READ_URI_PERMISSION,
- ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(uid)));
- } catch (RemoteException e) {
+ ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
+ } catch (RemoteException ignored) {
+ // Ignored because we're in same process
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -523,27 +525,32 @@
}
}
- private final void grantUriLocked(Uri uri, int primaryClipUid, String pkg, int userId) {
- long ident = Binder.clearCallingIdentity();
+ private final void grantUriLocked(Uri uri, int sourceUid, String targetPkg,
+ int targetUserId) {
+ if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
+
+ final long ident = Binder.clearCallingIdentity();
try {
- int sourceUserId = ContentProvider.getUserIdFromUri(uri, userId);
- uri = ContentProvider.getUriWithoutUserId(uri);
- mAm.grantUriPermissionFromOwner(mPermissionOwner, primaryClipUid, pkg,
- uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, userId);
- } catch (RemoteException e) {
+ mAm.grantUriPermissionFromOwner(mPermissionOwner, sourceUid, targetPkg,
+ ContentProvider.getUriWithoutUserId(uri),
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)),
+ targetUserId);
+ } catch (RemoteException ignored) {
+ // Ignored because we're in same process
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- private final void grantItemLocked(ClipData.Item item, int primaryClipUid, String pkg,
- int userId) {
+ private final void grantItemLocked(ClipData.Item item, int sourceUid, String targetPkg,
+ int targetUserId) {
if (item.getUri() != null) {
- grantUriLocked(item.getUri(), primaryClipUid, pkg, userId);
+ grantUriLocked(item.getUri(), sourceUid, targetPkg, targetUserId);
}
Intent intent = item.getIntent();
if (intent != null && intent.getData() != null) {
- grantUriLocked(intent.getData(), primaryClipUid, pkg, userId);
+ grantUriLocked(intent.getData(), sourceUid, targetPkg, targetUserId);
}
}
@@ -576,28 +583,29 @@
}
}
- private final void revokeUriLocked(Uri uri) {
- int userId = ContentProvider.getUserIdFromUri(uri,
- UserHandle.getUserId(Binder.getCallingUid()));
- long ident = Binder.clearCallingIdentity();
+ private final void revokeUriLocked(Uri uri, int sourceUid) {
+ if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
+
+ final long ident = Binder.clearCallingIdentity();
try {
- uri = ContentProvider.getUriWithoutUserId(uri);
- mAm.revokeUriPermissionFromOwner(mPermissionOwner, uri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- userId);
- } catch (RemoteException e) {
+ mAm.revokeUriPermissionFromOwner(mPermissionOwner,
+ ContentProvider.getUriWithoutUserId(uri),
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
+ } catch (RemoteException ignored) {
+ // Ignored because we're in same process
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- private final void revokeItemLocked(ClipData.Item item) {
+ private final void revokeItemLocked(ClipData.Item item, int sourceUid) {
if (item.getUri() != null) {
- revokeUriLocked(item.getUri());
+ revokeUriLocked(item.getUri(), sourceUid);
}
Intent intent = item.getIntent();
if (intent != null && intent.getData() != null) {
- revokeUriLocked(intent.getData());
+ revokeUriLocked(intent.getData(), sourceUid);
}
}
@@ -607,7 +615,7 @@
}
final int N = clipboard.primaryClip.getItemCount();
for (int i=0; i<N; i++) {
- revokeItemLocked(clipboard.primaryClip.getItemAt(i));
+ revokeItemLocked(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid);
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 2845383..c81624a 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -822,9 +822,9 @@
private void resolveStrictModeHostname() {
try {
// Do a blocking DNS resolution using the network-assigned nameservers.
- mPrivateDnsConfig = new PrivateDnsConfig(
- mPrivateDnsProviderHostname,
- mNetwork.getAllByName(mPrivateDnsProviderHostname));
+ final InetAddress[] ips = ResolvUtil.blockingResolveAllLocally(
+ mNetwork, mPrivateDnsProviderHostname);
+ mPrivateDnsConfig = new PrivateDnsConfig(mPrivateDnsProviderHostname, ips);
} catch (UnknownHostException uhe) {
mPrivateDnsConfig = null;
}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index decae18..a55870f 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1980,6 +1980,9 @@
}
static String formatTime(long time) {
+ if (time == 0) {
+ return "N/A";
+ }
Time tobj = new Time();
tobj.set(time);
return tobj.format("%Y-%m-%d %H:%M:%S");
@@ -2334,13 +2337,28 @@
pw.print("]");
pw.println();
+ pw.println(" Per source last syncs:");
+ for (int j = 0; j < SyncStorageEngine.SOURCES.length; j++) {
+ pw.print(" ");
+ pw.print(String.format("%8s", SyncStorageEngine.SOURCES[j]));
+ pw.print(" Success: ");
+ pw.print(formatTime(event.second.perSourceLastSuccessTimes[j]));
+
+ pw.print(" Failure: ");
+ pw.println(formatTime(event.second.perSourceLastFailureTimes[j]));
+ }
+
+ pw.println(" Last syncs:");
for (int j = 0; j < event.second.getEventCount(); j++) {
- pw.print(" ");
+ pw.print(" ");
pw.print(formatTime(event.second.getEventTime(j)));
pw.print(' ');
pw.print(event.second.getEvent(j));
pw.println();
}
+ if (event.second.getEventCount() == 0) {
+ pw.println(" N/A");
+ }
}
}
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index f54a9a0..6a343f8 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -128,8 +128,13 @@
public static final long NOT_IN_BACKOFF_MODE = -1;
- /** String names for the sync source types. */
- public static final String[] SOURCES = { "OTHER",
+ /**
+ * String names for the sync source types.
+ *
+ * KEEP THIS AND {@link SyncStatusInfo#SOURCE_COUNT} IN SYNC.
+ */
+ public static final String[] SOURCES = {
+ "OTHER",
"LOCAL",
"POLL",
"USER",
@@ -1231,12 +1236,7 @@
if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) {
writeStatusNow = true;
}
- status.lastSuccessTime = lastSyncTime;
- status.lastSuccessSource = item.source;
- status.lastFailureTime = 0;
- status.lastFailureSource = -1;
- status.lastFailureMesg = null;
- status.initialFailureTime = 0;
+ status.setLastSuccess(item.source, lastSyncTime);
ds.successCount++;
ds.successTime += elapsedTime;
} else if (!MESG_CANCELED.equals(resultMessage)) {
@@ -1246,12 +1246,8 @@
status.totalStats.numFailures++;
status.todayStats.numFailures++;
- status.lastFailureTime = lastSyncTime;
- status.lastFailureSource = item.source;
- status.lastFailureMesg = resultMessage;
- if (status.initialFailureTime == 0) {
- status.initialFailureTime = lastSyncTime;
- }
+ status.setLastFailure(item.source, lastSyncTime, resultMessage);
+
ds.failureCount++;
ds.failureTime += elapsedTime;
} else {
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 905c7e3..407fad0 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -282,13 +282,14 @@
}
Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED,
userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness,
- powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig));
+ powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig,
+ mInjector.currentTimeMillis()));
m.sendToTarget();
}
private void handleBrightnessChanged(float brightness, boolean userInitiated,
float powerBrightnessFactor, boolean isUserSetBrightness,
- boolean isDefaultBrightnessConfig) {
+ boolean isDefaultBrightnessConfig, long timestamp) {
BrightnessChangeEvent.Builder builder;
synchronized (mDataCollectionLock) {
@@ -309,7 +310,7 @@
builder = new BrightnessChangeEvent.Builder();
builder.setBrightness(brightness);
- builder.setTimeStamp(mInjector.currentTimeMillis());
+ builder.setTimeStamp(timestamp);
builder.setPowerBrightnessFactor(powerBrightnessFactor);
builder.setUserBrightnessPoint(isUserSetBrightness);
builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig);
@@ -813,7 +814,7 @@
boolean userInitiatedChange = (msg.arg1 == 1);
handleBrightnessChanged(values.brightness, userInitiatedChange,
values.powerBrightnessFactor, values.isUserSetBrightness,
- values.isDefaultBrightnessConfig);
+ values.isDefaultBrightnessConfig, values.timestamp);
break;
case MSG_START_SENSOR_LISTENER:
startSensorListener();
@@ -830,13 +831,16 @@
final float powerBrightnessFactor;
final boolean isUserSetBrightness;
final boolean isDefaultBrightnessConfig;
+ final long timestamp;
BrightnessChangeValues(float brightness, float powerBrightnessFactor,
- boolean isUserSetBrightness, boolean isDefaultBrightnessConfig) {
+ boolean isUserSetBrightness, boolean isDefaultBrightnessConfig,
+ long timestamp) {
this.brightness = brightness;
this.powerBrightnessFactor = powerBrightnessFactor;
this.isUserSetBrightness = isUserSetBrightness;
this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
+ this.timestamp = timestamp;
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 6490964..61d67b7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -17,6 +17,7 @@
package com.android.server.net;
import android.net.Network;
+import android.net.NetworkTemplate;
import android.telephony.SubscriptionPlan;
import java.util.Set;
@@ -58,6 +59,11 @@
*/
public abstract SubscriptionPlan getSubscriptionPlan(Network network);
+ /**
+ * Return the active {@link SubscriptionPlan} for the given template.
+ */
+ public abstract SubscriptionPlan getSubscriptionPlan(NetworkTemplate template);
+
public static final int QUOTA_TYPE_JOBS = 1;
public static final int QUOTA_TYPE_MULTIPATH = 2;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 5b8e8c0..5bd7c0b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -189,6 +189,7 @@
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -198,6 +199,7 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.DataUnit;
+import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.Range;
@@ -228,6 +230,7 @@
import com.android.server.SystemConfig;
import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -249,6 +252,7 @@
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Objects;
@@ -415,7 +419,6 @@
final Object mNetworkPoliciesSecondLock = new Object();
@GuardedBy("allLocks") volatile boolean mSystemReady;
- volatile boolean mSystemReallyReady;
@GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackground;
@GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictPower;
@@ -510,7 +513,6 @@
/** Map from network ID to last observed meteredness state */
@GuardedBy("mNetworkPoliciesSecondLock")
private final SparseBooleanArray mNetworkMetered = new SparseBooleanArray();
-
/** Map from network ID to last observed roaming state */
@GuardedBy("mNetworkPoliciesSecondLock")
private final SparseBooleanArray mNetworkRoaming = new SparseBooleanArray();
@@ -519,6 +521,13 @@
@GuardedBy("mNetworkPoliciesSecondLock")
private final SparseIntArray mNetIdToSubId = new SparseIntArray();
+ /** Map from subId to subscriberId as of last update */
+ @GuardedBy("mNetworkPoliciesSecondLock")
+ private final SparseArray<String> mSubIdToSubscriberId = new SparseArray<>();
+ /** Set of all merged subscriberId as of last update */
+ @GuardedBy("mNetworkPoliciesSecondLock")
+ private String[] mMergedSubscriberIds = EmptyArray.STRING;
+
/**
* Indicates the uids restricted by admin from accessing metered data. It's a mapping from
* userId to restricted uids which belong to that user.
@@ -843,6 +852,16 @@
new NetworkRequest.Builder().build(), mNetworkCallback);
mUsageStats.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
+
+ // Listen for subscriber changes
+ mContext.getSystemService(SubscriptionManager.class).addOnSubscriptionsChangedListener(
+ new OnSubscriptionsChangedListener(mHandler.getLooper()) {
+ @Override
+ public void onSubscriptionsChanged() {
+ updateNetworksInternal();
+ }
+ });
+
// tell systemReady() that the service has been initialized
initCompleteSignal.countDown();
} finally {
@@ -868,7 +887,6 @@
Thread.currentThread().interrupt();
throw new IllegalStateException("Service " + TAG + " init interrupted", e);
}
- mSystemReallyReady = true;
}
final private IUidObserver mUidObserver = new IUidObserver.Stub() {
@@ -1114,7 +1132,7 @@
final long now = mClock.millis();
for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
- final int subId = findRelevantSubId(policy.template);
+ final int subId = findRelevantSubIdNL(policy.template);
// ignore policies that aren't relevant to user
if (subId == INVALID_SUBSCRIPTION_ID) continue;
@@ -1245,14 +1263,11 @@
* @return relevant subId, or {@link #INVALID_SUBSCRIPTION_ID} when no
* matching subId found.
*/
- private int findRelevantSubId(NetworkTemplate template) {
- final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
- final SubscriptionManager sub = mContext.getSystemService(SubscriptionManager.class);
-
+ private int findRelevantSubIdNL(NetworkTemplate template) {
// Mobile template is relevant when any active subscriber matches
- final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList());
- for (int subId : subIds) {
- final String subscriberId = tele.getSubscriberId(subId);
+ for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
+ final int subId = mSubIdToSubscriberId.keyAt(i);
+ final String subscriberId = mSubIdToSubscriberId.valueAt(i);
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
true);
@@ -1407,23 +1422,29 @@
public void onReceive(Context context, Intent intent) {
// on background handler thread, and verified CONNECTIVITY_INTERNAL
// permission above.
-
- if (!mSystemReallyReady) return;
- synchronized (mUidRulesFirstLock) {
- synchronized (mNetworkPoliciesSecondLock) {
- ensureActiveMobilePolicyAL();
- normalizePoliciesNL();
- updateNetworkEnabledNL();
- updateNetworkRulesNL();
- updateNotificationsNL();
- }
- }
+ updateNetworksInternal();
}
};
+ private void updateNetworksInternal() {
+ // Get all of our cross-process communication with telephony out of
+ // the way before we acquire internal locks.
+ updateSubscriptions();
+
+ synchronized (mUidRulesFirstLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ ensureActiveMobilePolicyAL();
+ normalizePoliciesNL();
+ updateNetworkEnabledNL();
+ updateNetworkRulesNL();
+ updateNotificationsNL();
+ }
+ }
+ }
+
@VisibleForTesting
public void updateNetworks() throws InterruptedException {
- mConnReceiver.onReceive(null, null);
+ updateNetworksInternal();
final CountDownLatch latch = new CountDownLatch(1);
mHandler.post(() -> {
latch.countDown();
@@ -1438,14 +1459,11 @@
* @param subId that has its associated NetworkPolicy updated if necessary
* @return if any policies were updated
*/
- private boolean maybeUpdateMobilePolicyCycleAL(int subId) {
+ private boolean maybeUpdateMobilePolicyCycleAL(int subId, String subscriberId) {
if (LOGV) Slog.v(TAG, "maybeUpdateMobilePolicyCycleAL()");
- boolean policyUpdated = false;
- final String subscriberId = mContext.getSystemService(TelephonyManager.class)
- .getSubscriberId(subId);
-
// find and update the mobile NetworkPolicy for this subscriber id
+ boolean policyUpdated = false;
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true);
for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
@@ -1568,15 +1586,21 @@
return;
}
final int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, -1);
- final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
- final String subscriberId = tele.getSubscriberId(subId);
+
+ // Get all of our cross-process communication with telephony out of
+ // the way before we acquire internal locks.
+ updateSubscriptions();
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
- final boolean added = ensureActiveMobilePolicyAL(subId, subscriberId);
- if (added) return;
- final boolean updated = maybeUpdateMobilePolicyCycleAL(subId);
- if (!updated) return;
+ final String subscriberId = mSubIdToSubscriberId.get(subId, null);
+ if (subscriberId != null) {
+ ensureActiveMobilePolicyAL(subId, subscriberId);
+ maybeUpdateMobilePolicyCycleAL(subId, subscriberId);
+ } else {
+ Slog.wtf(TAG, "Missing subscriberId for subId " + subId);
+ }
+
// update network and notification rules, as the data cycle changed and it's
// possible that we should be triggering warnings/limits now
handleNetworkPoliciesUpdateAL(true);
@@ -1659,20 +1683,29 @@
if (template.getMatchRule() == MATCH_MOBILE) {
// If mobile data usage hits the limit or if the user resumes the data, we need to
// notify telephony.
- final SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
- final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
- final int[] subIds = ArrayUtils.defeatNullable(sm.getActiveSubscriptionIdList());
- for (int subId : subIds) {
- final String subscriberId = tm.getSubscriberId(subId);
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
- true);
- // Template is matched when subscriber id matches.
- if (template.matches(probeIdent)) {
- tm.setPolicyDataEnabled(enabled, subId);
+ final IntArray matchingSubIds = new IntArray();
+ synchronized (mNetworkPoliciesSecondLock) {
+ for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
+ final int subId = mSubIdToSubscriberId.keyAt(i);
+ final String subscriberId = mSubIdToSubscriberId.valueAt(i);
+
+ final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
+ true);
+ // Template is matched when subscriber id matches.
+ if (template.matches(probeIdent)) {
+ matchingSubIds.add(subId);
+ }
}
}
+
+ // Only talk with telephony outside of locks
+ final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ for (int i = 0; i < matchingSubIds.size(); i++) {
+ final int subId = matchingSubIds.get(i);
+ tm.setPolicyDataEnabled(enabled, subId);
+ }
}
}
@@ -1693,6 +1726,46 @@
}
/**
+ * Examine all currently active subscriptions from
+ * {@link SubscriptionManager#getActiveSubscriptionIdList()} and update
+ * internal data structures.
+ * <p>
+ * Callers <em>must not</em> hold any locks when this method called.
+ */
+ void updateSubscriptions() {
+ if (LOGV) Slog.v(TAG, "updateSubscriptions()");
+ Trace.traceBegin(TRACE_TAG_NETWORK, "updateSubscriptions");
+
+ final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ final SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
+
+ final int[] subIds = ArrayUtils.defeatNullable(sm.getActiveSubscriptionIdList());
+ final String[] mergedSubscriberIds = ArrayUtils.defeatNullable(tm.getMergedSubscriberIds());
+
+ final SparseArray<String> subIdToSubscriberId = new SparseArray<>(subIds.length);
+ for (int subId : subIds) {
+ final String subscriberId = tm.getSubscriberId(subId);
+ if (!TextUtils.isEmpty(subscriberId)) {
+ subIdToSubscriberId.put(subId, subscriberId);
+ } else {
+ Slog.wtf(TAG, "Missing subscriberId for subId " + subId);
+ }
+ }
+
+ synchronized (mNetworkPoliciesSecondLock) {
+ mSubIdToSubscriberId.clear();
+ for (int i = 0; i < subIdToSubscriberId.size(); i++) {
+ mSubIdToSubscriberId.put(subIdToSubscriberId.keyAt(i),
+ subIdToSubscriberId.valueAt(i));
+ }
+
+ mMergedSubscriberIds = mergedSubscriberIds;
+ }
+
+ Trace.traceEnd(TRACE_TAG_NETWORK);
+ }
+
+ /**
* Examine all connected {@link NetworkState}, looking for
* {@link NetworkPolicy} that need to be enforced. When matches found, set
* remaining quota based on usage cycle and historical stats.
@@ -1885,12 +1958,10 @@
if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyAL()");
if (mSuppressDefaultPolicy) return;
- final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
- final SubscriptionManager sub = mContext.getSystemService(SubscriptionManager.class);
+ for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
+ final int subId = mSubIdToSubscriberId.keyAt(i);
+ final String subscriberId = mSubIdToSubscriberId.valueAt(i);
- final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList());
- for (int subId : subIds) {
- final String subscriberId = tele.getSubscriberId(subId);
ensureActiveMobilePolicyAL(subId, subscriberId);
}
}
@@ -2633,14 +2704,11 @@
}
private void normalizePoliciesNL(NetworkPolicy[] policies) {
- final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
- final String[] merged = tele.getMergedSubscriberIds();
-
mNetworkPolicy.clear();
for (NetworkPolicy policy : policies) {
// When two normalized templates conflict, prefer the most
// restrictive policy
- policy.template = NetworkTemplate.normalize(policy.template, merged);
+ policy.template = NetworkTemplate.normalize(policy.template, mMergedSubscriberIds);
final NetworkPolicy existing = mNetworkPolicy.get(policy.template);
if (existing == null || existing.compareTo(policy) > 0) {
if (existing != null) {
@@ -3092,10 +3160,14 @@
mSubscriptionPlans.put(subId, plans);
mSubscriptionPlansOwner.put(subId, callingPackage);
- final String subscriberId = mContext.getSystemService(TelephonyManager.class)
- .getSubscriberId(subId);
- ensureActiveMobilePolicyAL(subId, subscriberId);
- maybeUpdateMobilePolicyCycleAL(subId);
+ final String subscriberId = mSubIdToSubscriberId.get(subId, null);
+ if (subscriberId != null) {
+ ensureActiveMobilePolicyAL(subId, subscriberId);
+ maybeUpdateMobilePolicyCycleAL(subId, subscriberId);
+ } else {
+ Slog.wtf(TAG, "Missing subscriberId for subId " + subId);
+ }
+
handleNetworkPoliciesUpdateAL(true);
}
}
@@ -3213,6 +3285,21 @@
fout.decreaseIndent();
fout.println();
+ fout.println("Active subscriptions:");
+ fout.increaseIndent();
+ for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
+ final int subId = mSubIdToSubscriberId.keyAt(i);
+ final String subscriberId = mSubIdToSubscriberId.valueAt(i);
+
+ fout.println(subId + "=" + NetworkIdentity.scrubSubscriberId(subscriberId));
+ }
+ fout.decreaseIndent();
+
+ fout.println();
+ fout.println("Merged subscriptions: "
+ + Arrays.toString(NetworkIdentity.scrubSubscriberId(mMergedSubscriberIds)));
+
+ fout.println();
fout.println("Policy for UIDs:");
fout.increaseIndent();
int size = mUidPolicy.size();
@@ -4810,6 +4897,14 @@
}
@Override
+ public SubscriptionPlan getSubscriptionPlan(NetworkTemplate template) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ final int subId = findRelevantSubIdNL(template);
+ return getPrimarySubscriptionPlanLocked(subId);
+ }
+ }
+
+ @Override
public long getSubscriptionOpportunisticQuota(Network network, int quotaType) {
final long quotaBytes;
synchronized (mNetworkPoliciesSecondLock) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 7ee17bc..ae7058d 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -116,7 +116,6 @@
import android.provider.Settings.Global;
import android.service.NetworkInterfaceProto;
import android.service.NetworkStatsServiceDumpProto;
-import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
@@ -679,22 +678,12 @@
private SubscriptionPlan resolveSubscriptionPlan(NetworkTemplate template, int flags) {
SubscriptionPlan plan = null;
if ((flags & NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN) != 0
- && (template.getMatchRule() == NetworkTemplate.MATCH_MOBILE)
&& mSettings.getAugmentEnabled()) {
if (LOGD) Slog.d(TAG, "Resolving plan for " + template);
final long token = Binder.clearCallingIdentity();
try {
- final SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
- final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
- for (int subId : sm.getActiveSubscriptionIdList()) {
- if (template.matchesSubscriberId(tm.getSubscriberId(subId))) {
- if (LOGD) Slog.d(TAG, "Found active matching subId " + subId);
- final List<SubscriptionPlan> plans = sm.getSubscriptionPlans(subId);
- if (!plans.isEmpty()) {
- plan = plans.get(0);
- }
- }
- }
+ plan = LocalServices.getService(NetworkPolicyManagerInternal.class)
+ .getSubscriptionPlan(template);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index b61a27a..8be8450 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -23,11 +23,14 @@
public interface NotificationDelegate {
void onSetDisabled(int status);
void onClearAll(int callingUid, int callingPid, int userId);
- void onNotificationClick(int callingUid, int callingPid, String key);
- void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex);
+ void onNotificationClick(int callingUid, int callingPid, String key,
+ NotificationVisibility nv);
+ void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex,
+ NotificationVisibility nv);
void onNotificationClear(int callingUid, int callingPid,
String pkg, String tag, int id, int userId, String key,
- @NotificationStats.DismissalSurface int dismissalSurface);
+ @NotificationStats.DismissalSurface int dismissalSurface,
+ NotificationVisibility nv);
void onNotificationError(int callingUid, int callingPid,
String pkg, String tag, int id,
int uid, int initialPid, String message, int userId);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 8a64308..5948864 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -37,7 +37,6 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
-import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_NULL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.NotificationListenerService
@@ -228,7 +227,6 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
@@ -315,7 +313,6 @@
private ICompanionDeviceManager mCompanionManager;
private AccessibilityManager mAccessibilityManager;
private IDeviceIdleController mDeviceIdleController;
- private IBinder mPermissionOwner;
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
@@ -693,7 +690,7 @@
}
@Override
- public void onNotificationClick(int callingUid, int callingPid, String key) {
+ public void onNotificationClick(int callingUid, int callingPid, String key, NotificationVisibility nv) {
exitIdle();
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
@@ -704,22 +701,26 @@
final long now = System.currentTimeMillis();
MetricsLogger.action(r.getLogMaker(now)
.setCategory(MetricsEvent.NOTIFICATION_ITEM)
- .setType(MetricsEvent.TYPE_ACTION));
+ .setType(MetricsEvent.TYPE_ACTION)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count));
EventLogTags.writeNotificationClicked(key,
- r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
+ r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
+ nv.rank, nv.count);
StatusBarNotification sbn = r.sbn;
cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
sbn.getId(), Notification.FLAG_AUTO_CANCEL,
Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
- REASON_CLICK, null);
+ REASON_CLICK, nv.rank, nv.count, null);
+ nv.recycle();
reportUserInteraction(r);
}
}
@Override
public void onNotificationActionClick(int callingUid, int callingPid, String key,
- int actionIndex) {
+ int actionIndex, NotificationVisibility nv) {
exitIdle();
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
@@ -731,9 +732,13 @@
MetricsLogger.action(r.getLogMaker(now)
.setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION)
.setType(MetricsEvent.TYPE_ACTION)
- .setSubtype(actionIndex));
+ .setSubtype(actionIndex)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count));
EventLogTags.writeNotificationActionClicked(key, actionIndex,
- r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
+ r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
+ nv.rank, nv.count);
+ nv.recycle();
reportUserInteraction(r);
}
}
@@ -741,7 +746,8 @@
@Override
public void onNotificationClear(int callingUid, int callingPid,
String pkg, String tag, int id, int userId, String key,
- @NotificationStats.DismissalSurface int dismissalSurface) {
+ @NotificationStats.DismissalSurface int dismissalSurface,
+ NotificationVisibility nv) {
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
@@ -750,7 +756,8 @@
}
cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
- true, userId, REASON_CANCEL, null);
+ true, userId, REASON_CANCEL, nv.rank, nv.count,null);
+ nv.recycle();
}
@Override
@@ -810,7 +817,7 @@
mMetricsLogger.write(logMaker);
}
}
- r.setVisibility(true, nv.rank);
+ r.setVisibility(true, nv.rank, nv.count);
nv.recycle();
}
// Note that we might receive this event after notifications
@@ -820,7 +827,7 @@
for (NotificationVisibility nv : noLongerVisibleKeys) {
NotificationRecord r = mNotificationsByKey.get(nv.key);
if (r == null) continue;
- r.setVisibility(false, nv.rank);
+ r.setVisibility(false, nv.rank, nv.count);
nv.recycle();
}
}
@@ -1369,12 +1376,6 @@
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
mDpm = dpm;
- try {
- mPermissionOwner = mAm.newUriPermissionOwner("notification");
- } catch (RemoteException e) {
- Slog.w(TAG, "AM dead", e);
- }
-
mHandler = new WorkerHandler(looper);
mRankingThread.start();
String[] extractorNames;
@@ -2396,6 +2397,11 @@
}
@Override
+ public boolean areChannelsBypassingDnd() {
+ return mRankingHelper.areChannelsBypassingDnd();
+ }
+
+ @Override
public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
checkCallerIsSystem();
@@ -3277,7 +3283,6 @@
policy = new Policy(policy.priorityCategories,
policy.priorityCallSenders, policy.priorityMessageSenders,
newVisualEffects);
-
ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion, policy);
mZenModeHelper.setNotificationPolicy(policy);
} catch (RemoteException e) {
@@ -3957,7 +3962,7 @@
sbn.getNotification().flags =
(r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
mRankingHelper.sort(mNotificationList);
- mListeners.notifyPostedLocked(r, sbn /* oldSbn */);
+ mListeners.notifyPostedLocked(r, r);
}
};
@@ -4423,8 +4428,6 @@
// Make sure we don't lose the foreground service state.
notification.flags |=
old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
- // revoke uri permissions for changed uris
- revokeUriPermissions(r, old);
r.isUpdate = true;
r.setInterruptive(isVisuallyInterruptive(old, r));
}
@@ -4443,7 +4446,7 @@
if (notification.getSmallIcon() != null) {
StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
- mListeners.notifyPostedLocked(r, oldSbn);
+ mListeners.notifyPostedLocked(r, old);
if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
mHandler.post(new Runnable() {
@Override
@@ -5289,6 +5292,12 @@
@GuardedBy("mNotificationLock")
private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
boolean wasPosted, String listenerName) {
+ cancelNotificationLocked(r, sendDelete, reason, -1, -1, wasPosted, listenerName);
+ }
+
+ @GuardedBy("mNotificationLock")
+ private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
+ int rank, int count, boolean wasPosted, String listenerName) {
final String canceledKey = r.getKey();
// Record caller.
@@ -5298,9 +5307,6 @@
r.recordDismissalSurface(NotificationStats.DISMISSAL_OTHER);
}
- // Revoke permissions
- revokeUriPermissions(null, r);
-
// tell the app
if (sendDelete) {
if (r.getNotification().deleteIntent != null) {
@@ -5390,33 +5396,125 @@
mArchive.record(r.sbn);
final long now = System.currentTimeMillis();
- MetricsLogger.action(r.getLogMaker(now)
+ final LogMaker logMaker = r.getLogMaker(now)
.setCategory(MetricsEvent.NOTIFICATION_ITEM)
.setType(MetricsEvent.TYPE_DISMISS)
- .setSubtype(reason));
+ .setSubtype(reason);
+ if (rank != -1 && count != -1) {
+ logMaker.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, count);
+ }
+ MetricsLogger.action(logMaker);
EventLogTags.writeNotificationCanceled(canceledKey, reason,
- r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now), listenerName);
+ r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
+ rank, count, listenerName);
}
- void revokeUriPermissions(NotificationRecord newRecord, NotificationRecord oldRecord) {
- Set<Uri> oldUris = oldRecord.getNotificationUris();
- Set<Uri> newUris = newRecord == null ? new HashSet<>() : newRecord.getNotificationUris();
- oldUris.removeAll(newUris);
+ @VisibleForTesting
+ void updateUriPermissions(@Nullable NotificationRecord newRecord,
+ @Nullable NotificationRecord oldRecord, String targetPkg, int targetUserId) {
+ final String key = (newRecord != null) ? newRecord.getKey() : oldRecord.getKey();
+ if (DBG) Slog.d(TAG, key + ": updating permissions");
- long ident = Binder.clearCallingIdentity();
- try {
- for (Uri uri : oldUris) {
- if (uri != null) {
- int notiUserId = oldRecord.getUserId();
- int sourceUserId = notiUserId == USER_ALL ? USER_SYSTEM
- : ContentProvider.getUserIdFromUri(uri, notiUserId);
- uri = ContentProvider.getUriWithoutUserId(uri);
- mAm.revokeUriPermissionFromOwner(mPermissionOwner,
- uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId);
+ final ArraySet<Uri> newUris = (newRecord != null) ? newRecord.getGrantableUris() : null;
+ final ArraySet<Uri> oldUris = (oldRecord != null) ? oldRecord.getGrantableUris() : null;
+
+ // Shortcut when no Uris involved
+ if (newUris == null && oldUris == null) {
+ return;
+ }
+
+ // Inherit any existing owner
+ IBinder permissionOwner = null;
+ if (newRecord != null && permissionOwner == null) {
+ permissionOwner = newRecord.permissionOwner;
+ }
+ if (oldRecord != null && permissionOwner == null) {
+ permissionOwner = oldRecord.permissionOwner;
+ }
+
+ // If we have Uris to grant, but no owner yet, go create one
+ if (newUris != null && permissionOwner == null) {
+ try {
+ if (DBG) Slog.d(TAG, key + ": creating owner");
+ permissionOwner = mAm.newUriPermissionOwner("NOTIF:" + key);
+ } catch (RemoteException ignored) {
+ // Ignored because we're in same process
+ }
+ }
+
+ // If we have no Uris to grant, but an existing owner, go destroy it
+ if (newUris == null && permissionOwner != null) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (DBG) Slog.d(TAG, key + ": destroying owner");
+ mAm.revokeUriPermissionFromOwner(permissionOwner, null, ~0,
+ UserHandle.getUserId(oldRecord.getUid()));
+ permissionOwner = null;
+ } catch (RemoteException ignored) {
+ // Ignored because we're in same process
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ // Grant access to new Uris
+ if (newUris != null && permissionOwner != null) {
+ for (int i = 0; i < newUris.size(); i++) {
+ final Uri uri = newUris.valueAt(i);
+ if (oldUris == null || !oldUris.contains(uri)) {
+ if (DBG) Slog.d(TAG, key + ": granting " + uri);
+ grantUriPermission(permissionOwner, uri, newRecord.getUid(), targetPkg,
+ targetUserId);
}
}
- } catch (RemoteException e) {
- Log.e(TAG, "Count not revoke uri permissions", e);
+ }
+
+ // Revoke access to old Uris
+ if (oldUris != null && permissionOwner != null) {
+ for (int i = 0; i < oldUris.size(); i++) {
+ final Uri uri = oldUris.valueAt(i);
+ if (newUris == null || !newUris.contains(uri)) {
+ if (DBG) Slog.d(TAG, key + ": revoking " + uri);
+ revokeUriPermission(permissionOwner, uri, oldRecord.getUid());
+ }
+ }
+ }
+
+ if (newRecord != null) {
+ newRecord.permissionOwner = permissionOwner;
+ }
+ }
+
+ private void grantUriPermission(IBinder owner, Uri uri, int sourceUid, String targetPkg,
+ int targetUserId) {
+ if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mAm.grantUriPermissionFromOwner(owner, sourceUid, targetPkg,
+ ContentProvider.getUriWithoutUserId(uri),
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)),
+ targetUserId);
+ } catch (RemoteException ignored) {
+ // Ignored because we're in same process
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void revokeUriPermission(IBinder owner, Uri uri, int sourceUid) {
+ if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mAm.revokeUriPermissionFromOwner(owner,
+ ContentProvider.getUriWithoutUserId(uri),
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
+ } catch (RemoteException ignored) {
+ // Ignored because we're in same process
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -5430,6 +5528,18 @@
final String pkg, final String tag, final int id,
final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
final int userId, final int reason, final ManagedServiceInfo listener) {
+ cancelNotification(callingUid, callingPid, pkg, tag, id, mustHaveFlags, mustNotHaveFlags,
+ sendDelete, userId, reason, -1 /* rank */, -1 /* count */, listener);
+ }
+
+ /**
+ * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
+ * and none of the {@code mustNotHaveFlags}.
+ */
+ void cancelNotification(final int callingUid, final int callingPid,
+ final String pkg, final String tag, final int id,
+ final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
+ final int userId, final int reason, int rank, int count, final ManagedServiceInfo listener) {
// In enqueueNotificationInternal notifications are added by scheduling the
// work on the worker handler. Hence, we also schedule the cancel on this
@@ -5463,7 +5573,7 @@
// Cancel the notification.
boolean wasPosted = removeFromNotificationListsLocked(r);
- cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
+ cancelNotificationLocked(r, sendDelete, reason, rank, count, wasPosted, listenerName);
cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
sendDelete, null);
updateLightsLocked();
@@ -6309,8 +6419,8 @@
* but isn't anymore.
*/
@GuardedBy("mNotificationLock")
- public void notifyPostedLocked(NotificationRecord r, StatusBarNotification oldSbn) {
- notifyPostedLocked(r, oldSbn, true);
+ public void notifyPostedLocked(NotificationRecord r, NotificationRecord old) {
+ notifyPostedLocked(r, old, true);
}
/**
@@ -6318,14 +6428,13 @@
* targetting <= O_MR1
*/
@GuardedBy("mNotificationLock")
- private void notifyPostedLocked(NotificationRecord r, StatusBarNotification oldSbn,
+ private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
boolean notifyAllListeners) {
// Lazily initialized snapshots of the notification.
StatusBarNotification sbn = r.sbn;
+ StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
TrimCache trimCache = new TrimCache(sbn);
- Set<Uri> uris = r.getNotificationUris();
-
for (final ManagedServiceInfo info : getServices()) {
boolean sbnVisible = isVisibleToListener(sbn, info);
boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
@@ -6363,10 +6472,12 @@
continue;
}
- grantUriPermissions(uris, sbn.getUserId(), info.component.getPackageName(),
- info.userid);
+ // Grant access before listener is notified
+ final int targetUserId = (info.userid == UserHandle.USER_ALL)
+ ? UserHandle.USER_SYSTEM : info.userid;
+ updateUriPermissions(r, old, info.component.getPackageName(), targetUserId);
- final StatusBarNotification sbnToPost = trimCache.ForListener(info);
+ final StatusBarNotification sbnToPost = trimCache.ForListener(info);
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -6376,28 +6487,6 @@
}
}
- private void grantUriPermissions(Set<Uri> uris, int notiUserId, String listenerPkg,
- int listenerUserId) {
- long ident = Binder.clearCallingIdentity();
- try {
- for (Uri uri : uris) {
- if (uri != null) {
- int sourceUserId = notiUserId == USER_ALL ? USER_SYSTEM
- : ContentProvider.getUserIdFromUri(uri, notiUserId);
- uri = ContentProvider.getUriWithoutUserId(uri);
- mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(),
- listenerPkg,
- uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId,
- listenerUserId == USER_ALL ? USER_SYSTEM : listenerUserId);
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Count not grant uri permission to " + listenerPkg, e);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
/**
* asynchronously notify all listeners about a removed notification
*/
@@ -6405,6 +6494,7 @@
public void notifyRemovedLocked(NotificationRecord r, int reason,
NotificationStats notificationStats) {
final StatusBarNotification sbn = r.sbn;
+
// make a copy in case changes are made to the underlying Notification object
// NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
// notification
@@ -6439,6 +6529,11 @@
}
});
}
+
+ // Revoke access after all listeners have been updated
+ mHandler.post(() -> {
+ updateUriPermissions(null, r, null, UserHandle.USER_SYSTEM);
+ });
}
/**
@@ -6540,7 +6635,7 @@
int numChangedNotifications = changedNotifications.size();
for (int i = 0; i < numChangedNotifications; i++) {
NotificationRecord rec = changedNotifications.get(i);
- mListeners.notifyPostedLocked(rec, rec.sbn, false);
+ mListeners.notifyPostedLocked(rec, rec, false);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 9745be3..2aec3ea 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -16,22 +16,28 @@
package com.android.server.notification;
import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.service.notification.NotificationListenerService.Ranking
.USER_SENTIMENT_NEUTRAL;
import static android.service.notification.NotificationListenerService.Ranking
.USER_SENTIMENT_POSITIVE;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
@@ -39,9 +45,11 @@
import android.media.AudioSystem;
import android.metrics.LogMaker;
import android.net.Uri;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.Parcelable;
+import android.os.IBinder;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.Adjustment;
@@ -53,7 +61,6 @@
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
-import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.widget.RemoteViews;
@@ -62,6 +69,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
import java.io.PrintWriter;
import java.lang.reflect.Array;
@@ -69,7 +77,6 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
/**
* Holds data about notifications that should not be shared with the
@@ -88,11 +95,13 @@
static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final int MAX_LOGTAG_LENGTH = 35;
final StatusBarNotification sbn;
+ final int mTargetSdkVersion;
final int mOriginalFlags;
private final Context mContext;
NotificationUsageStats.SingleNotificationStats stats;
boolean isCanceled;
+ IBinder permissionOwner;
// These members are used by NotificationSignalExtractors
// to communicate with the ranking module.
@@ -156,10 +165,13 @@
* conjunction with user sentiment calculation.
*/
private boolean mIsAppImportanceLocked;
+ private ArraySet<Uri> mGrantableUris;
public NotificationRecord(Context context, StatusBarNotification sbn,
NotificationChannel channel) {
this.sbn = sbn;
+ mTargetSdkVersion = LocalServices.getService(PackageManagerInternal.class)
+ .getPackageTargetSdkVersion(sbn.getPackageName());
mOriginalFlags = sbn.getNotification().flags;
mRankingTimeMs = calculateRankingTimeMs(0L);
mCreationTimeMs = sbn.getPostTime();
@@ -176,20 +188,14 @@
mAdjustments = new ArrayList<>();
mStats = new NotificationStats();
calculateUserSentiment();
+ calculateGrantableUris();
}
private boolean isPreChannelsNotification() {
- try {
- if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
- final ApplicationInfo applicationInfo =
- mContext.getPackageManager().getApplicationInfoAsUser(sbn.getPackageName(),
- 0, UserHandle.getUserId(sbn.getUid()));
- if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
- return true;
- }
+ if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
+ if (mTargetSdkVersion < Build.VERSION_CODES.O) {
+ return true;
}
- } catch (NameNotFoundException e) {
- Slog.e(TAG, "Can't find package", e);
}
return false;
}
@@ -377,6 +383,7 @@
public String getKey() { return sbn.getKey(); }
/** @deprecated Use {@link #getUser()} instead. */
public int getUserId() { return sbn.getUserId(); }
+ public int getUid() { return sbn.getUid(); }
void dump(ProtoOutputStream proto, long fieldId, boolean redact, int state) {
final long token = proto.start(fieldId);
@@ -778,14 +785,15 @@
/**
* Set the visibility of the notification.
*/
- public void setVisibility(boolean visible, int rank) {
+ public void setVisibility(boolean visible, int rank, int count) {
final long now = System.currentTimeMillis();
mVisibleSinceMs = visible ? now : mVisibleSinceMs;
stats.onVisibilityChanged(visible);
MetricsLogger.action(getLogMaker(now)
.setCategory(MetricsEvent.NOTIFICATION_ITEM)
.setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
- .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank));
+ .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, count));
if (visible) {
setSeen();
MetricsLogger.histogram(mContext, "note_freshness", getFreshnessMs(now));
@@ -998,40 +1006,72 @@
mHasSeenSmartReplies = hasSeenSmartReplies;
}
- public Set<Uri> getNotificationUris() {
- Notification notification = getNotification();
- Set<Uri> uris = new ArraySet<>();
+ /**
+ * @return all {@link Uri} that should have permission granted to whoever
+ * will be rendering it. This list has already been vetted to only
+ * include {@link Uri} that the enqueuing app can grant.
+ */
+ public @Nullable ArraySet<Uri> getGrantableUris() {
+ return mGrantableUris;
+ }
- if (notification.sound != null) {
- uris.add(notification.sound);
- }
+ /**
+ * Collect all {@link Uri} that should have permission granted to whoever
+ * will be rendering it.
+ */
+ private void calculateGrantableUris() {
+ final Notification notification = getNotification();
+ notification.visitUris((uri) -> {
+ visitGrantableUri(uri);
+ });
+
if (notification.getChannelId() != null) {
NotificationChannel channel = getChannel();
- if (channel != null && channel.getSound() != null) {
- uris.add(channel.getSound());
+ if (channel != null) {
+ visitGrantableUri(channel.getSound());
}
}
- if (notification.extras.containsKey(Notification.EXTRA_AUDIO_CONTENTS_URI)) {
- uris.add(notification.extras.getParcelable(Notification.EXTRA_AUDIO_CONTENTS_URI));
- }
- if (notification.extras.containsKey(Notification.EXTRA_BACKGROUND_IMAGE_URI)) {
- uris.add(notification.extras.getParcelable(Notification.EXTRA_BACKGROUND_IMAGE_URI));
- }
- if (Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
- Parcelable[] newMessages =
- notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
- List<Notification.MessagingStyle.Message> messages
- = Notification.MessagingStyle.Message.getMessagesFromBundleArray(newMessages);
- Parcelable[] histMessages =
- notification.extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
- messages.addAll(
- Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages));
- for (Notification.MessagingStyle.Message message : messages) {
- uris.add(message.getDataUri());
- }
- }
+ }
- return uris;
+ /**
+ * Note the presence of a {@link Uri} that should have permission granted to
+ * whoever will be rendering it.
+ * <p>
+ * If the enqueuing app has the ability to grant access, it will be added to
+ * {@link #mGrantableUris}. Otherwise, this will either log or throw
+ * {@link SecurityException} depending on target SDK of enqueuing app.
+ */
+ private void visitGrantableUri(Uri uri) {
+ if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
+
+ // We can't grant Uri permissions from system
+ final int sourceUid = sbn.getUid();
+ if (sourceUid == android.os.Process.SYSTEM_UID) return;
+
+ final IActivityManager am = ActivityManager.getService();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ // This will throw SecurityException if caller can't grant
+ am.checkGrantUriPermission(sourceUid, null,
+ ContentProvider.getUriWithoutUserId(uri),
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
+
+ if (mGrantableUris == null) {
+ mGrantableUris = new ArraySet<>();
+ }
+ mGrantableUris.add(uri);
+ } catch (RemoteException ignored) {
+ // Ignored because we're in same process
+ } catch (SecurityException e) {
+ if (mTargetSdkVersion >= Build.VERSION_CODES.P) {
+ throw e;
+ } else {
+ Log.w(TAG, "Ignoring " + uri + " from " + sourceUid + ": " + e.getMessage());
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
public LogMaker getLogMaker(long now) {
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 89bd660..febce31 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -17,13 +17,6 @@
import static android.app.NotificationManager.IMPORTANCE_NONE;
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.Notification;
@@ -52,6 +45,13 @@
import android.util.SparseBooleanArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -65,11 +65,11 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
public class RankingHelper implements RankingConfig {
private static final String TAG = "RankingHelper";
@@ -127,12 +127,15 @@
private String mPermissionControllerPackageName;
private String mServicesSystemSharedLibPackageName;
private String mSharedSystemSharedLibPackageName;
+ private boolean mAreChannelsBypassingDnd;
+ private ZenModeHelper mZenModeHelper;
public RankingHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames) {
mContext = context;
mRankingHandler = rankingHandler;
mPm = pm;
+ mZenModeHelper= zenHelper;
mPreliminaryComparator = new NotificationComparator(mContext);
@@ -159,6 +162,7 @@
}
getSignatures();
+ updateChannelsBypassingDnd();
}
@SuppressWarnings("unchecked")
@@ -648,7 +652,12 @@
// system apps and dnd access apps can bypass dnd if the user hasn't changed any
// fields on the channel yet
if (existing.getUserLockedFields() == 0 && (isSystemApp || hasDndAccess)) {
- existing.setBypassDnd(channel.canBypassDnd());
+ boolean bypassDnd = channel.canBypassDnd();
+ existing.setBypassDnd(bypassDnd);
+
+ if (bypassDnd != mAreChannelsBypassingDnd) {
+ updateChannelsBypassingDnd();
+ }
}
updateConfig();
@@ -675,6 +684,9 @@
}
r.channels.put(channel.getId(), channel);
+ if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
+ updateChannelsBypassingDnd();
+ }
MetricsLogger.action(getChannelLog(channel, pkg).setType(
MetricsProto.MetricsEvent.TYPE_OPEN));
}
@@ -781,6 +793,10 @@
// only log if there are real changes
MetricsLogger.action(getChannelLog(updatedChannel, pkg));
}
+
+ if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd) {
+ updateChannelsBypassingDnd();
+ }
updateConfig();
}
@@ -814,6 +830,10 @@
LogMaker lm = getChannelLog(channel, pkg);
lm.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
MetricsLogger.action(lm);
+
+ if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
+ updateChannelsBypassingDnd();
+ }
}
}
@@ -1026,6 +1046,45 @@
return count;
}
+ public void updateChannelsBypassingDnd() {
+ synchronized (mRecords) {
+ final int numRecords = mRecords.size();
+ for (int recordIndex = 0; recordIndex < numRecords; recordIndex++) {
+ final Record r = mRecords.valueAt(recordIndex);
+ final int numChannels = r.channels.size();
+
+ for (int channelIndex = 0; channelIndex < numChannels; channelIndex++) {
+ NotificationChannel channel = r.channels.valueAt(channelIndex);
+ if (!channel.isDeleted() && channel.canBypassDnd()) {
+ if (!mAreChannelsBypassingDnd) {
+ mAreChannelsBypassingDnd = true;
+ updateZenPolicy(true);
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ if (mAreChannelsBypassingDnd) {
+ mAreChannelsBypassingDnd = false;
+ updateZenPolicy(false);
+ }
+ }
+
+ public void updateZenPolicy(boolean areChannelsBypassingDnd) {
+ NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
+ mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
+ policy.priorityCategories, policy.priorityCallSenders,
+ policy.priorityMessageSenders, policy.suppressedVisualEffects,
+ (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
+ : 0)));
+ }
+
+ public boolean areChannelsBypassingDnd() {
+ return mAreChannelsBypassingDnd;
+ }
+
/**
* Sets importance.
*/
@@ -1225,12 +1284,16 @@
if (r.showBadge != DEFAULT_SHOW_BADGE) {
record.put("showBadge", Boolean.valueOf(r.showBadge));
}
+ JSONArray channels = new JSONArray();
for (NotificationChannel channel : r.channels.values()) {
- record.put("channel", channel.toJson());
+ channels.put(channel.toJson());
}
+ record.put("channels", channels);
+ JSONArray groups = new JSONArray();
for (NotificationChannelGroup group : r.groups.values()) {
- record.put("group", group.toJson());
+ groups.put(group.toJson());
}
+ record.put("groups", groups);
} catch (JSONException e) {
// pass
}
diff --git a/services/core/java/com/android/server/os/SchedulingPolicyService.java b/services/core/java/com/android/server/os/SchedulingPolicyService.java
index e0b8426..c64e745 100644
--- a/services/core/java/com/android/server/os/SchedulingPolicyService.java
+++ b/services/core/java/com/android/server/os/SchedulingPolicyService.java
@@ -18,8 +18,10 @@
import android.content.pm.PackageManager;
import android.os.Binder;
+import android.os.IBinder;
import android.os.ISchedulingPolicyService;
import android.os.Process;
+import android.os.RemoteException;
import android.util.Log;
/**
@@ -35,7 +37,36 @@
private static final int PRIORITY_MIN = 1;
private static final int PRIORITY_MAX = 3;
+ private static final String[] MEDIA_PROCESS_NAMES = new String[] {
+ "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
+ };
+ private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ requestCpusetBoost(false /*enable*/, null /*client*/);
+ }
+ };
+ // Current process that received a cpuset boost
+ private int mBoostedPid = -1;
+ // Current client registered to the death recipient
+ private IBinder mClient;
+
public SchedulingPolicyService() {
+ // system_server (our host) could have crashed before. The app may not survive
+ // it, but mediaserver/media.codec could have, and mediaserver probably tried
+ // to disable the boost while we were dead.
+ // We do a restore of media.codec to default cpuset upon service restart to
+ // catch this case. We can't leave media.codec in boosted state, because we've
+ // lost the death recipient of mClient from mediaserver after the restart,
+ // if mediaserver dies in the future we won't have a notification to reset.
+ // (Note that if mediaserver thinks we're in boosted state before the crash,
+ // the state could go out of sync temporarily until mediaserver enables/disable
+ // boost next time, but this won't be a big issue.)
+ int[] nativePids = Process.getPidsForCommands(MEDIA_PROCESS_NAMES);
+ if (nativePids != null && nativePids.length == 1) {
+ mBoostedPid = nativePids[0];
+ disableCpusetBoost(nativePids[0]);
+ }
}
// TODO(b/35196900) We should pass the period in time units, rather
@@ -74,6 +105,94 @@
return PackageManager.PERMISSION_GRANTED;
}
+ // Request to move media.codec process between SP_FOREGROUND and SP_TOP_APP.
+ public int requestCpusetBoost(boolean enable, IBinder client) {
+ if (!isPermitted()) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ int[] nativePids = Process.getPidsForCommands(MEDIA_PROCESS_NAMES);
+ if (nativePids == null || nativePids.length != 1) {
+ Log.e(TAG, "requestCpusetBoost: can't find media.codec process");
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ synchronized (mDeathRecipient) {
+ if (enable) {
+ return enableCpusetBoost(nativePids[0], client);
+ } else {
+ return disableCpusetBoost(nativePids[0]);
+ }
+ }
+ }
+
+ private int enableCpusetBoost(int pid, IBinder client) {
+ if (mBoostedPid == pid) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+
+ // The mediacodec process has changed, clean up the old pid and
+ // client before we boost the new process, so that the state
+ // is left clean if things go wrong.
+ mBoostedPid = -1;
+ if (mClient != null) {
+ try {
+ mClient.unlinkToDeath(mDeathRecipient, 0);
+ } catch (Exception e) {
+ } finally {
+ mClient = null;
+ }
+ }
+
+ try {
+ client.linkToDeath(mDeathRecipient, 0);
+
+ Log.i(TAG, "Moving " + pid + " to group " + Process.THREAD_GROUP_TOP_APP);
+ Process.setProcessGroup(pid, Process.THREAD_GROUP_TOP_APP);
+
+ mBoostedPid = pid;
+ mClient = client;
+
+ return PackageManager.PERMISSION_GRANTED;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed enableCpusetBoost: " + e);
+ try {
+ // unlink if things go wrong and don't crash.
+ client.unlinkToDeath(mDeathRecipient, 0);
+ } catch (Exception e1) {}
+ }
+
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ private int disableCpusetBoost(int pid) {
+ int boostedPid = mBoostedPid;
+
+ // Clean up states first.
+ mBoostedPid = -1;
+ if (mClient != null) {
+ try {
+ mClient.unlinkToDeath(mDeathRecipient, 0);
+ } catch (Exception e) {
+ } finally {
+ mClient = null;
+ }
+ }
+
+ // Try restore the old thread group, no need to fail as the
+ // mediacodec process could be dead just now.
+ if (boostedPid == pid) {
+ try {
+ Log.i(TAG, "Moving " + pid + " back to group default");
+ Process.setProcessGroup(pid, Process.THREAD_GROUP_DEFAULT);
+ } catch (Exception e) {
+ Log.w(TAG, "Couldn't move pid " + pid + " back to group default");
+ }
+ }
+
+ return PackageManager.PERMISSION_GRANTED;
+ }
+
private boolean isPermitted() {
// schedulerservice hidl
if (Binder.getCallingPid() == Process.myPid()) {
@@ -81,9 +200,10 @@
}
switch (Binder.getCallingUid()) {
- case Process.AUDIOSERVER_UID: // fastcapture, fastmixer
+ case Process.AUDIOSERVER_UID: // fastcapture, fastmixer
+ case Process.MEDIA_UID: // mediaserver
case Process.CAMERASERVER_UID: // camera high frame rate recording
- case Process.BLUETOOTH_UID: // Bluetooth audio playback
+ case Process.BLUETOOTH_UID: // Bluetooth audio playback
return true;
default:
return false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 485d0f2..a200231 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2462,7 +2462,7 @@
installer, mInstallLock);
mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock,
dexManagerListener);
- mArtManagerService = new ArtManagerService(this, installer, mInstallLock);
+ mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
@@ -23581,6 +23581,16 @@
SigningDetails.CertCapabilities.INSTALLED_DATA);
}
+ @Override
+ public boolean hasSignatureCapability(int serverUid, int clientUid,
+ @SigningDetails.CertCapabilities int capability) {
+ SigningDetails serverSigningDetails = getSigningDetails(serverUid);
+ SigningDetails clientSigningDetails = getSigningDetails(clientUid);
+ return serverSigningDetails.checkCapability(clientSigningDetails, capability)
+ || clientSigningDetails.hasAncestorOrSelf(serverSigningDetails);
+
+ }
+
private SigningDetails getSigningDetails(@NonNull String packageName) {
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -23591,6 +23601,22 @@
}
}
+ private SigningDetails getSigningDetails(int uid) {
+ synchronized (mPackages) {
+ final int appId = UserHandle.getAppId(uid);
+ final Object obj = mSettings.getUserIdLPr(appId);
+ if (obj != null) {
+ if (obj instanceof SharedUserSetting) {
+ return ((SharedUserSetting) obj).signatures.mSigningDetails;
+ } else if (obj instanceof PackageSetting) {
+ final PackageSetting ps = (PackageSetting) obj;
+ return ps.signatures.mSigningDetails;
+ }
+ }
+ return SigningDetails.UNKNOWN;
+ }
+ }
+
@Override
public int getPermissionFlagsTEMP(String permName, String packageName, int userId) {
return PackageManagerService.this.getPermissionFlags(permName, packageName, userId);
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 9c2ad463..1d5c580 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -16,8 +16,9 @@
package com.android.server.pm.dex;
-import android.Manifest;
import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
@@ -38,7 +39,9 @@
import android.os.UserHandle;
import android.system.Os;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Slog;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
@@ -47,14 +50,17 @@
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PackageManagerServiceCompilerMapping;
+
import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
-import java.io.File;
-import java.io.FileNotFoundException;
+
import libcore.io.IoUtils;
import libcore.util.NonNull;
import libcore.util.Nullable;
+import java.io.File;
+import java.io.FileNotFoundException;
+
/**
* A system service that provides access to runtime and compiler artifacts.
*
@@ -69,9 +75,7 @@
*/
public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
private static final String TAG = "ArtManagerService";
-
- private static boolean DEBUG = false;
- private static boolean DEBUG_IGNORE_PERMISSIONS = false;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// Package name used to create the profile directory layout when
// taking a snapshot of the boot image profile.
@@ -79,6 +83,7 @@
// Profile name used for the boot image profile.
private static final String BOOT_IMAGE_PROFILE_NAME = "android.prof";
+ private final Context mContext;
private final IPackageManager mPackageManager;
private final Object mInstallLock;
@GuardedBy("mInstallLock")
@@ -90,7 +95,9 @@
verifyTronLoggingConstants();
}
- public ArtManagerService(IPackageManager pm, Installer installer, Object installLock) {
+ public ArtManagerService(Context context, IPackageManager pm, Installer installer,
+ Object installLock) {
+ mContext = context;
mPackageManager = pm;
mInstaller = installer;
mInstallLock = installLock;
@@ -99,9 +106,37 @@
LocalServices.addService(ArtManagerInternal.class, new ArtManagerInternalImpl());
}
+ private boolean checkPermission(int callingUid, String callingPackage) {
+ // Callers always need this permission
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_RUNTIME_PROFILES, TAG);
+
+ // Callers also need the ability to read usage statistics
+ switch (mContext.getSystemService(AppOpsManager.class)
+ .noteOp(AppOpsManager.OP_GET_USAGE_STATS, callingUid, callingPackage)) {
+ case AppOpsManager.MODE_ALLOWED:
+ return true;
+ case AppOpsManager.MODE_DEFAULT:
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.PACKAGE_USAGE_STATS, TAG);
+ return true;
+ default:
+ return false;
+ }
+ }
+
@Override
public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
- @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback) {
+ @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback,
+ String callingPackage) {
+ if (!checkPermission(Binder.getCallingUid(), callingPackage)) {
+ try {
+ callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+ } catch (RemoteException ignored) {
+ }
+ return;
+ }
+
// Sanity checks on the arguments.
Preconditions.checkNotNull(callback);
@@ -111,9 +146,8 @@
Preconditions.checkStringNotEmpty(packageName);
}
- // Verify that the caller has the right permissions and that the runtime profiling is
- // enabled. The call to isRuntimePermissions will checkReadRuntimeProfilePermission.
- if (!isRuntimeProfilingEnabled(profileType)) {
+ // Verify that runtime profiling is enabled.
+ if (!isRuntimeProfilingEnabled(profileType, callingPackage)) {
throw new IllegalStateException("Runtime profiling is not enabled for " + profileType);
}
@@ -231,9 +265,10 @@
}
@Override
- public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
- // Verify that the caller has the right permissions.
- checkReadRuntimeProfilePermission();
+ public boolean isRuntimeProfilingEnabled(@ProfileType int profileType, String callingPackage) {
+ if (!checkPermission(Binder.getCallingUid(), callingPackage)) {
+ return false;
+ }
switch (profileType) {
case ArtManager.PROFILE_APPS :
@@ -306,27 +341,6 @@
}
/**
- * Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}.
- * If not, it throws a {@link SecurityException}.
- */
- private void checkReadRuntimeProfilePermission() {
- if (DEBUG_IGNORE_PERMISSIONS) {
- return;
- }
- try {
- int result = mPackageManager.checkUidPermission(
- Manifest.permission.READ_RUNTIME_PROFILES, Binder.getCallingUid());
- if (result != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("You need "
- + Manifest.permission.READ_RUNTIME_PROFILES
- + " permission to snapshot profiles.");
- }
- } catch (RemoteException e) {
- // Should not happen.
- }
- }
-
- /**
* Prepare the application profiles.
* For all code paths:
* - create the current primary profile to save time at app startup time.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 920e77f..bd4210c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -6091,6 +6091,14 @@
&& (!isNavBarVirtKey || mNavBarVirtualKeyHapticFeedbackEnabled)
&& event.getRepeatCount() == 0;
+ // Cancel any pending remote recents animations before handling the button itself. In the
+ // case where we are going home and the recents animation has already started, just cancel
+ // the recents animation, leaving the home stack in place for the pending start activity
+ if (isNavBarVirtKey && !down) {
+ boolean isHomeKey = keyCode == KeyEvent.KEYCODE_HOME;
+ mActivityManagerInternal.cancelRecentsAnimation(!isHomeKey);
+ }
+
// Handle special keys.
switch (keyCode) {
case KeyEvent.KEYCODE_BACK: {
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 401f74c..d1b48480 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -426,7 +426,7 @@
return;
}
try {
- sStatsd.writeDataToDisk();
+ sStatsd.informDeviceShutdown(true);
} catch (Exception e) {
Slog.w(TAG, "Failed to inform statsd of a shutdown event.", e);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 36fa868..738b0ca 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1000,27 +1000,27 @@
}
@Override
- public void onNotificationClick(String key) {
+ public void onNotificationClick(String key, NotificationVisibility nv) {
enforceStatusBarService();
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
long identity = Binder.clearCallingIdentity();
try {
- mNotificationDelegate.onNotificationClick(callingUid, callingPid, key);
+ mNotificationDelegate.onNotificationClick(callingUid, callingPid, key, nv);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
- public void onNotificationActionClick(String key, int actionIndex) {
+ public void onNotificationActionClick(String key, int actionIndex, NotificationVisibility nv) {
enforceStatusBarService();
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onNotificationActionClick(callingUid, callingPid, key,
- actionIndex);
+ actionIndex, nv);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1044,14 +1044,14 @@
@Override
public void onNotificationClear(String pkg, String tag, int id, int userId, String key,
- @NotificationStats.DismissalSurface int dismissalSurface) {
+ @NotificationStats.DismissalSurface int dismissalSurface, NotificationVisibility nv) {
enforceStatusBarService();
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
long identity = Binder.clearCallingIdentity();
try {
- mNotificationDelegate.onNotificationClear(
- callingUid, callingPid, pkg, tag, id, userId, key, dismissalSurface);
+ mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId,
+ key, dismissalSurface, nv);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index d123099c..0ccbb25 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -674,7 +674,7 @@
final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>();
- int mCurrentUserId;
+ int mCurrentUserId = UserHandle.USER_NULL;
boolean mInAmbientMode;
static class WallpaperData {
@@ -1166,7 +1166,11 @@
mIPackageManager = AppGlobals.getPackageManager();
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mMonitor = new MyPackageMonitor();
- mMonitor.register(context, null, UserHandle.ALL, true);
+ mColorsChangedListeners = new SparseArray<>();
+ }
+
+ void initialize() {
+ mMonitor.register(mContext, null, UserHandle.ALL, true);
getWallpaperDir(UserHandle.USER_SYSTEM).mkdirs();
// Initialize state from the persistent store, then guarantee that the
@@ -1174,8 +1178,6 @@
// it from defaults if necessary.
loadSettingsLocked(UserHandle.USER_SYSTEM, false);
getWallpaperSafeLocked(UserHandle.USER_SYSTEM, FLAG_SYSTEM);
-
- mColorsChangedListeners = new SparseArray<>();
}
private static File getWallpaperDir(int userId) {
@@ -1193,6 +1195,8 @@
void systemReady() {
if (DEBUG) Slog.v(TAG, "systemReady");
+ initialize();
+
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
// If we think we're going to be using the system image wallpaper imagery, make
// sure we have something to render
@@ -1344,6 +1348,9 @@
final WallpaperData systemWallpaper;
final WallpaperData lockWallpaper;
synchronized (mLock) {
+ if (mCurrentUserId == userId) {
+ return;
+ }
mCurrentUserId = userId;
systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index c6f156b..a24ac21 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -20,6 +20,7 @@
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
+import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
@@ -1569,6 +1570,8 @@
a = null;
} else if (transit == TRANSIT_KEYGUARD_UNOCCLUDE && !enter) {
a = loadAnimationRes(lp, com.android.internal.R.anim.wallpaper_open_exit);
+ } else if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) {
+ a = null;
} else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
|| transit == TRANSIT_TASK_OPEN
|| transit == TRANSIT_TASK_TO_FRONT)) {
@@ -2157,8 +2160,10 @@
setAppTransition(transit, flags);
}
// We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
- // relies on the fact that we always execute a Keyguard transition after preparing one.
- else if (!alwaysKeepCurrent && !isKeyguardTransit(transit)) {
+ // relies on the fact that we always execute a Keyguard transition after preparing one. We
+ // also don't want to change away from a crashing transition.
+ else if (!alwaysKeepCurrent && !isKeyguardTransit(transit)
+ && transit != TRANSIT_CRASHING_ACTIVITY_CLOSE) {
if (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) {
// Opening a new task always supersedes a close for the anim.
setAppTransition(transit, flags);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 5676f58..a701d42 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1662,7 +1662,9 @@
}
SurfaceControl getAppAnimationLayer() {
- return getAppAnimationLayer(needsZBoost());
+ return getAppAnimationLayer(isActivityTypeHome() ? ANIMATION_LAYER_HOME
+ : needsZBoost() ? ANIMATION_LAYER_BOOSTED
+ : ANIMATION_LAYER_STANDARD);
}
@Override
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 79eb2c9..4fd31ff 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3190,6 +3190,7 @@
*/
SurfaceControl mAppAnimationLayer = null;
SurfaceControl mBoostedAppAnimationLayer = null;
+ SurfaceControl mHomeAppAnimationLayer = null;
/**
* Given that the split-screen divider does not have an AppWindowToken, it
@@ -3552,6 +3553,7 @@
int layer = 0;
int layerForAnimationLayer = 0;
int layerForBoostedAnimationLayer = 0;
+ int layerForHomeAnimationLayer = 0;
for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
for (int i = 0; i < mChildren.size(); i++) {
@@ -3578,6 +3580,9 @@
layerForBoostedAnimationLayer = layer++;
}
}
+ if (state == HOME_STACK_STATE) {
+ layerForHomeAnimationLayer = layer++;
+ }
}
if (mAppAnimationLayer != null) {
t.setLayer(mAppAnimationLayer, layerForAnimationLayer);
@@ -3585,11 +3590,22 @@
if (mBoostedAppAnimationLayer != null) {
t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
}
+ if (mHomeAppAnimationLayer != null) {
+ t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
+ }
}
@Override
- SurfaceControl getAppAnimationLayer(boolean boosted) {
- return boosted ? mBoostedAppAnimationLayer : mAppAnimationLayer;
+ SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) {
+ switch (animationLayer) {
+ case ANIMATION_LAYER_BOOSTED:
+ return mBoostedAppAnimationLayer;
+ case ANIMATION_LAYER_HOME:
+ return mHomeAppAnimationLayer;
+ case ANIMATION_LAYER_STANDARD:
+ default:
+ return mAppAnimationLayer;
+ }
}
SurfaceControl getSplitScreenDividerAnchor() {
@@ -3606,12 +3622,16 @@
mBoostedAppAnimationLayer = makeChildSurface(null)
.setName("boostedAnimationLayer")
.build();
+ mHomeAppAnimationLayer = makeChildSurface(null)
+ .setName("homeAnimationLayer")
+ .build();
mSplitScreenDividerAnchor = makeChildSurface(null)
.setName("splitScreenDividerAnchor")
.build();
getPendingTransaction()
.show(mAppAnimationLayer)
.show(mBoostedAppAnimationLayer)
+ .show(mHomeAppAnimationLayer)
.show(mSplitScreenDividerAnchor);
scheduleAnimation();
} else {
@@ -3619,6 +3639,8 @@
mAppAnimationLayer = null;
mBoostedAppAnimationLayer.destroy();
mBoostedAppAnimationLayer = null;
+ mHomeAppAnimationLayer.destroy();
+ mHomeAppAnimationLayer = null;
mSplitScreenDividerAnchor.destroy();
mSplitScreenDividerAnchor = null;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6478632..79b230d 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -369,7 +369,7 @@
}
void cancelAnimation(@ReorderMode int reorderMode, String reason) {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation()");
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason);
synchronized (mService.getWindowManagerLock()) {
if (mCanceled) {
// We've already canceled the animation
@@ -405,6 +405,14 @@
// Clear associated input consumers
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+
+ // We have deferred all notifications to the target app as a part of the recents animation,
+ // so if we are actually transitioning there, notify again here
+ if (mTargetAppToken != null) {
+ if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
+ mService.mAppTransition.notifyAppTransitionFinishedLocked(mTargetAppToken.token);
+ }
+ }
}
void scheduleFailsafe() {
@@ -469,6 +477,10 @@
return false;
}
+ boolean isTargetApp(AppWindowToken token) {
+ return mTargetAppToken != null && token == mTargetAppToken;
+ }
+
private boolean isTargetOverWallpaper() {
if (mTargetAppToken == null) {
return false;
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 1b06b2f..67ef471 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -83,7 +83,8 @@
*/
AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
Rect stackBounds) {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimationAdapter(): token=" + appWindowToken);
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimationAdapter(): token="
+ + appWindowToken);
final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
appWindowToken, position, stackBounds);
mPendingAnimations.add(adapter);
@@ -96,8 +97,9 @@
void goodToGo() {
if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo()");
if (mPendingAnimations.isEmpty() || mCanceled) {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): Animation finished before good to go, canceled="
- + mCanceled + " mPendingAnimations=" + mPendingAnimations.size());
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): Animation finished already,"
+ + " canceled=" + mCanceled
+ + " mPendingAnimations=" + mPendingAnimations.size());
onAnimationFinished();
return;
}
@@ -123,10 +125,6 @@
}
if (DEBUG_REMOTE_ANIMATIONS) {
Slog.d(TAG, "startAnimation(): Notify animation start:");
- for (int i = 0; i < mPendingAnimations.size(); i++) {
- Slog.d(TAG, "\t" + mPendingAnimations.get(i).mAppWindowToken);
- }
- } else if (DEBUG_APP_TRANSITIONS) {
writeStartDebugStatement();
}
});
@@ -166,7 +164,8 @@
if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tAdd token=" + wrapper.mAppWindowToken);
targets.add(target);
} else {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tRemove token=" + wrapper.mAppWindowToken);
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tRemove token="
+ + wrapper.mAppWindowToken);
// We can't really start an animation but we still need to make sure to finish the
// pending animation that was started by SurfaceAnimator
@@ -188,7 +187,8 @@
releaseFinishedCallback();
mService.openSurfaceTransaction();
try {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): Notify animation finished:");
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG,
+ "onAnimationFinished(): Notify animation finished:");
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 95223d8..f87538a 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -564,7 +564,7 @@
public SurfaceControl getAnimationLeashParent() {
// Reparent to the animation layer so that we aren't clipped by the non-minimized
// stack bounds, currently we only animate the task for the recents animation
- return getAppAnimationLayer(false /* boosted */);
+ return getAppAnimationLayer(ANIMATION_LAYER_STANDARD);
}
boolean isTaskAnimating() {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 60e7c0d..331a0bd 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -29,6 +29,8 @@
import static com.android.server.wm.WindowContainerProto.VISIBLE;
import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
@@ -60,6 +62,25 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
+ /** Animation layer that happens above all animating {@link TaskStack}s. */
+ static final int ANIMATION_LAYER_STANDARD = 0;
+
+ /** Animation layer that happens above all {@link TaskStack}s. */
+ static final int ANIMATION_LAYER_BOOSTED = 1;
+
+ /**
+ * Animation layer that is reserved for {@link WindowConfiguration#ACTIVITY_TYPE_HOME}
+ * activities that happens below all {@link TaskStack}s.
+ */
+ static final int ANIMATION_LAYER_HOME = 2;
+
+ @IntDef(prefix = { "ANIMATION_LAYER_" }, value = {
+ ANIMATION_LAYER_STANDARD,
+ ANIMATION_LAYER_BOOSTED,
+ ANIMATION_LAYER_HOME,
+ })
+ @interface AnimationLayer {}
+
static final int POSITION_TOP = Integer.MAX_VALUE;
static final int POSITION_BOTTOM = Integer.MIN_VALUE;
@@ -1125,15 +1146,12 @@
}
/**
- * @param boosted If true, returns an animation layer that happens above all {@link TaskStack}s
- * Otherwise, the layer will be positioned above all animating
- * {@link TaskStack}s.
* @return The layer on which all app animations are happening.
*/
- SurfaceControl getAppAnimationLayer(boolean boosted) {
+ SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) {
final WindowContainer parent = getParent();
if (parent != null) {
- return parent.getAppAnimationLayer(boosted);
+ return parent.getAppAnimationLayer(animationLayer);
}
return null;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 407312a..b1b026e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -864,10 +864,19 @@
} else {
atoken.updateReportedVisibilityLocked();
if (atoken.mEnteringAnimation) {
- atoken.mEnteringAnimation = false;
- try {
- mActivityManager.notifyEnterAnimationComplete(atoken.token);
- } catch (RemoteException e) {
+ if (getRecentsAnimationController() != null
+ && getRecentsAnimationController().isTargetApp(atoken)) {
+ // Currently running a recents animation, this will get called early because
+ // we show the recents animation target activity immediately when the
+ // animation starts. In this case, we should defer sending the finished
+ // callback until the animation successfully finishes
+ return;
+ } else {
+ atoken.mEnteringAnimation = false;
+ try {
+ mActivityManager.notifyEnterAnimationComplete(atoken.token);
+ } catch (RemoteException e) {
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 61ce062..6af730f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1088,6 +1088,8 @@
: Math.max(mFrame.bottom - mStableFrame.bottom, 0));
}
+ mDisplayCutout = displayCutout.calculateRelativeTo(mFrame);
+
// Offset the actual frame by the amount layout frame is off.
mFrame.offset(-layoutXDiff, -layoutYDiff);
mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
@@ -1095,10 +1097,6 @@
mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
mStableFrame.offset(-layoutXDiff, -layoutYDiff);
- // TODO(roosa): Figure out what frame exactly this needs to be calculated with.
- mDisplayCutout = displayCutout.calculateRelativeTo(mFrame);
-
-
mCompatFrame.set(mFrame);
if (mEnforceSizeCompat) {
// If there is a size compatibility scale being applied to the
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index b85f4a4..70287db 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
+import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
@@ -368,6 +369,10 @@
*/
private void overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit,
ArraySet<Integer> activityTypes) {
+ if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) {
+ // The crash transition has higher priority than any involved remote animations.
+ return;
+ }
if (animLpToken == null) {
return;
}
@@ -625,13 +630,12 @@
private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper,
boolean closingAppHasWallpaper) {
- // Given no app transition pass it through instead of a wallpaper transition
- if (transit == TRANSIT_NONE) {
- return TRANSIT_NONE;
- }
+ // Given no app transition pass it through instead of a wallpaper transition.
+ // Never convert the crashing transition.
// Never update the transition for the wallpaper if we are just docking from recents
- if (transit == TRANSIT_DOCK_TASK_FROM_RECENTS) {
- return TRANSIT_DOCK_TASK_FROM_RECENTS;
+ if (transit == TRANSIT_NONE || transit == TRANSIT_CRASHING_ACTIVITY_CLOSE
+ || transit == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+ return transit;
}
// if wallpaper is animating in or out set oldWallpaper to null else to wallpaper
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index d5ff2dd..9a97ba7 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -522,7 +522,7 @@
ICMP6_4_BYTE_LIFETIME_OFFSET, ICMP6_4_BYTE_LIFETIME_LEN);
}
- // Note that this parses RA and may throw IllegalArgumentException (from
+ // Note that this parses RA and may throw InvalidRaException (from
// Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException
// (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with
// specifications.
@@ -986,9 +986,8 @@
*/
@GuardedBy("this")
private ApfGenerator beginProgramLocked() throws IllegalInstructionException {
- ApfGenerator gen = new ApfGenerator();
- // This is guaranteed to return true because of the check in maybeCreate.
- gen.setApfVersion(mApfCapabilities.apfVersionSupported);
+ // This is guaranteed to succeed because of the check in maybeCreate.
+ ApfGenerator gen = new ApfGenerator(mApfCapabilities.apfVersionSupported);
// Here's a basic summary of what the initial program does:
//
@@ -1216,7 +1215,7 @@
// 1. the program generator will need its offsets adjusted.
// 2. the packet filter attached to our packet socket will need its offset adjusted.
if (apfCapabilities.apfPacketFormat != ARPHRD_ETHER) return null;
- if (!new ApfGenerator().setApfVersion(apfCapabilities.apfVersionSupported)) {
+ if (!ApfGenerator.supportsVersion(apfCapabilities.apfVersionSupported)) {
Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
return null;
}
diff --git a/services/net/java/android/net/apf/ApfGenerator.java b/services/net/java/android/net/apf/ApfGenerator.java
index ca8f727..99b2fc6 100644
--- a/services/net/java/android/net/apf/ApfGenerator.java
+++ b/services/net/java/android/net/apf/ApfGenerator.java
@@ -58,7 +58,9 @@
JLT(18), // Compare less than and branch, e.g. "jlt R0,5,label"
JSET(19), // Compare any bits set and branch, e.g. "jset R0,5,label"
JNEBS(20), // Compare not equal byte sequence, e.g. "jnebs R0,5,label,0x1122334455"
- EXT(21); // Followed by immediate indicating ExtendedOpcodes.
+ EXT(21), // Followed by immediate indicating ExtendedOpcodes.
+ LDDW(22), // Load 4 bytes from data memory address (register + immediate): "lddw R0, [5]R1"
+ STDW(23); // Store 4 bytes to data memory address (register + immediate): "stdw R0, [5]R1"
final int value;
@@ -355,19 +357,38 @@
*/
public static final int LAST_PREFILLED_MEMORY_SLOT = FILTER_AGE_MEMORY_SLOT;
+ // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h
+ private static final int MIN_APF_VERSION = 2;
+
private final ArrayList<Instruction> mInstructions = new ArrayList<Instruction>();
private final HashMap<String, Instruction> mLabels = new HashMap<String, Instruction>();
private final Instruction mDropLabel = new Instruction(Opcodes.LABEL);
private final Instruction mPassLabel = new Instruction(Opcodes.LABEL);
+ private final int mVersion;
private boolean mGenerated;
/**
- * Set version of APF instruction set to generate instructions for. Returns {@code true}
- * if generating for this version is supported, {@code false} otherwise.
+ * Creates an ApfGenerator instance which is able to emit instructions for the specified
+ * {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if
+ * the requested version is unsupported.
*/
- public boolean setApfVersion(int version) {
- // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h
- return version >= 2;
+ ApfGenerator(int version) throws IllegalInstructionException {
+ mVersion = version;
+ requireApfVersion(MIN_APF_VERSION);
+ }
+
+ /**
+ * Returns true if the specified {@code version} is supported by the ApfGenerator, otherwise
+ * false.
+ */
+ public static boolean supportsVersion(int version) {
+ return version >= MIN_APF_VERSION;
+ }
+
+ private void requireApfVersion(int minimumVersion) throws IllegalInstructionException {
+ if (mVersion < minimumVersion) {
+ throw new IllegalInstructionException("Requires APF >= " + minimumVersion);
+ }
}
private void addInstruction(Instruction instruction) {
@@ -819,6 +840,36 @@
}
/**
+ * Add an instruction to the end of the program to load 32 bits from the data memory into
+ * {@code register}. The source address is computed by adding the signed immediate
+ * @{code offset} to the other register.
+ * Requires APF v3 or greater.
+ */
+ public ApfGenerator addLoadData(Register destinationRegister, int offset)
+ throws IllegalInstructionException {
+ requireApfVersion(3);
+ Instruction instruction = new Instruction(Opcodes.LDDW, destinationRegister);
+ instruction.setSignedImm(offset);
+ addInstruction(instruction);
+ return this;
+ }
+
+ /**
+ * Add an instruction to the end of the program to store 32 bits from {@code register} into the
+ * data memory. The destination address is computed by adding the signed immediate
+ * @{code offset} to the other register.
+ * Requires APF v3 or greater.
+ */
+ public ApfGenerator addStoreData(Register sourceRegister, int offset)
+ throws IllegalInstructionException {
+ requireApfVersion(3);
+ Instruction instruction = new Instruction(Opcodes.STDW, sourceRegister);
+ instruction.setSignedImm(offset);
+ addInstruction(instruction);
+ return this;
+ }
+
+ /**
* Updates instruction offset fields using latest instruction sizes.
* @return current program length in bytes.
*/
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 87249df..9fdb31e 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -16,6 +16,7 @@
package android.net.ip;
+import com.android.internal.util.HexDump;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.WakeupMessage;
@@ -142,6 +143,12 @@
// Install an APF program to filter incoming packets.
public void installPacketFilter(byte[] filter) {}
+ // Asynchronously read back the APF program & data buffer from the wifi driver.
+ // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
+ // buffer. In response to this request, the driver returns the data buffer asynchronously
+ // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
+ public void startReadPacketFilter() {}
+
// If multicast filtering cannot be accomplished with APF, this function will be called to
// actuate multicast filtering using another means.
public void setFallbackMulticastFilter(boolean enabled) {}
@@ -248,6 +255,11 @@
log("installPacketFilter(byte[" + filter.length + "])");
}
@Override
+ public void startReadPacketFilter() {
+ mCallback.startReadPacketFilter();
+ log("startReadPacketFilter()");
+ }
+ @Override
public void setFallbackMulticastFilter(boolean enabled) {
mCallback.setFallbackMulticastFilter(enabled);
log("setFallbackMulticastFilter(" + enabled + ")");
@@ -559,6 +571,7 @@
private static final int CMD_SET_MULTICAST_FILTER = 9;
private static final int EVENT_PROVISIONING_TIMEOUT = 10;
private static final int EVENT_DHCPACTION_TIMEOUT = 11;
+ private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12;
private static final int MAX_LOG_RECORDS = 500;
private static final int MAX_PACKET_RECORDS = 100;
@@ -611,6 +624,7 @@
private ApfFilter mApfFilter;
private boolean mMulticastFiltering;
private long mStartTimeMillis;
+ private byte[] mApfDataSnapshot;
public static class Dependencies {
public INetworkManagementService getNMS() {
@@ -823,6 +837,10 @@
sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
}
+ public void readPacketFilterComplete(byte[] data) {
+ sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data);
+ }
+
/**
* Set the TCP buffer sizes to use.
*
@@ -863,6 +881,7 @@
final ProvisioningConfiguration provisioningConfig = mConfiguration;
final ApfCapabilities apfCapabilities = (provisioningConfig != null)
? provisioningConfig.mApfCapabilities : null;
+ final byte[] apfDataSnapshot = mApfDataSnapshot;
IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
pw.println(mTag + " APF dump:");
@@ -880,6 +899,14 @@
}
}
pw.decreaseIndent();
+ pw.println(mTag + " latest APF data snapshot: ");
+ pw.increaseIndent();
+ if (apfDataSnapshot != null) {
+ pw.println(HexDump.dumpHexString(apfDataSnapshot));
+ } else {
+ pw.println("No last snapshot.");
+ }
+ pw.decreaseIndent();
pw.println();
pw.println(mTag + " current ProvisioningConfiguration:");
@@ -1676,6 +1703,11 @@
break;
}
+ case EVENT_READ_PACKET_FILTER_COMPLETE: {
+ mApfDataSnapshot = (byte[]) msg.obj;
+ break;
+ }
+
case EVENT_DHCPACTION_TIMEOUT:
stopDhcpAction();
break;
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 45bee6e..75dc96f 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
@@ -63,6 +64,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@SmallTest
@@ -447,7 +449,7 @@
final long secondSensorTime = mInjector.currentTimeMillis();
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(3));
notifyBrightnessChanged(mTracker, brightness, true /*userInitiated*/,
- 0.5f /*powerPolicyDim(*/, true /*hasUserBrightnessPoints*/,
+ 0.5f /*powerBrightnessFactor*/, true /*hasUserBrightnessPoints*/,
false /*isDefaultBrightnessConfig*/);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
mTracker.writeEventsLocked(baos);
@@ -586,6 +588,48 @@
assertTrue(slice.getList().isEmpty());
}
+ @Test
+ public void testBackgroundHandlerDelay() {
+ final int brightness = 20;
+
+ // Setup tracker.
+ startTracker(mTracker);
+ mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
+
+ // Block handler from running.
+ final CountDownLatch latch = new CountDownLatch(1);
+ mInjector.mHandler.post(
+ () -> {
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ });
+
+ // Send an event.
+ long eventTime = mInjector.currentTimeMillis();
+ mTracker.notifyBrightnessChanged(brightness, true /*userInitiated*/,
+ 1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/,
+ false /*isDefaultBrightnessConfig*/);
+
+ // Time passes before handler can run.
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
+
+ // Let the handler run.
+ latch.countDown();
+ mInjector.waitForHandler();
+
+ List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
+ mTracker.stop();
+
+ // Check event was recorded with time it was sent rather than handler ran.
+ assertEquals(1, events.size());
+ BrightnessChangeEvent event = events.get(0);
+ assertEquals(eventTime, event.timeStamp);
+ }
+
private InputStream getInputStream(String data) {
return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 4638635..5a56332 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -441,6 +441,28 @@
assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetRight(), 0);
}
+ @Test
+ public void testDisplayCutout_tempInsetBounds() {
+ // Regular fullscreen task and window
+ TaskWithBounds task = new TaskWithBounds(new Rect(0, -500, 1000, 1500));
+ task.mFullscreenForTest = false;
+ task.mInsetBounds.set(0, 0, 1000, 2000);
+ WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+ 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), pf.width(), pf.height());
+
+ w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout, false);
+
+ assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetTop(), 50);
+ assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetBottom(), 0);
+ assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetLeft(), 0);
+ assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetRight(), 0);
+ }
+
private WindowStateWithTask createWindow(Task task, int width, int height) {
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
attrs.width = width;
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index 4c70466..aa3135f 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -28,6 +28,7 @@
<uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
<uses-permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
index f534b5c..345c1d7 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
@@ -13,15 +13,25 @@
*/
package com.android.server;
-import android.content.Context;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.PackageManagerInternal;
+import android.os.Build;
import android.support.test.InstrumentationRegistry;
import android.testing.TestableContext;
import org.junit.Before;
import org.junit.Rule;
-
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
public class UiServiceTestCase {
+ @Mock protected PackageManagerInternal mPmi;
+
+ protected static final String PKG_N_MR1 = "com.example.n_mr1";
+ protected static final String PKG_O = "com.example.o";
+
@Rule
public final TestableContext mContext =
new TestableContext(InstrumentationRegistry.getContext(), null);
@@ -32,7 +42,24 @@
@Before
public void setup() {
+ MockitoAnnotations.initMocks(this);
+
// Share classloader to allow package access.
System.setProperty("dexmaker.share_classloader", "true");
+
+ // Assume some default packages
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPmi);
+ when(mPmi.getPackageTargetSdkVersion(anyString()))
+ .thenAnswer((iom) -> {
+ switch ((String) iom.getArgument(0)) {
+ case PKG_N_MR1:
+ return Build.VERSION_CODES.N_MR1;
+ case PKG_O:
+ return Build.VERSION_CODES.O;
+ default:
+ return Build.VERSION_CODES.CUR_DEVELOPMENT;
+ }
+ });
}
}
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 9d5d263..0c2928a9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -73,6 +73,7 @@
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
import android.content.ComponentName;
+import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -80,6 +81,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.graphics.Color;
+import android.media.AudioAttributes;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Binder;
@@ -88,6 +90,7 @@
import android.os.IBinder;
import android.os.Process;
import android.os.UserHandle;
+import android.provider.MediaStore;
import android.provider.Settings.Secure;
import android.service.notification.Adjustment;
import android.service.notification.NotificationListenerService;
@@ -622,8 +625,6 @@
mBinderService.getActiveNotifications(PKG);
assertEquals(0, notifs.length);
assertEquals(0, mService.getNotificationRecordCount());
- verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(
- any(), any(), anyInt(), anyInt());
}
@Test
@@ -642,7 +643,6 @@
ArgumentCaptor<NotificationStats> captor = ArgumentCaptor.forClass(NotificationStats.class);
verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), captor.capture());
assertEquals(NotificationStats.DISMISSAL_OTHER, captor.getValue().getDismissalSurface());
- verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt());
}
@Test
@@ -657,7 +657,6 @@
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(0, notifs.length);
assertEquals(0, mService.getNotificationRecordCount());
- verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt());
}
@Test
@@ -671,7 +670,6 @@
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(0, notifs.length);
assertEquals(0, mService.getNotificationRecordCount());
- verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt());
}
@Test
@@ -693,7 +691,6 @@
ArgumentCaptor<NotificationStats> captor = ArgumentCaptor.forClass(NotificationStats.class);
verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), captor.capture());
assertEquals(NotificationStats.DISMISSAL_OTHER, captor.getValue().getDismissalSurface());
- verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt());
}
@Test
@@ -712,7 +709,6 @@
mBinderService.cancelAllNotifications(PKG, parent.sbn.getUserId());
waitForIdle();
assertEquals(0, mService.getNotificationRecordCount());
- verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt());
}
@Test
@@ -726,7 +722,6 @@
waitForIdle();
assertEquals(0, mService.getNotificationRecordCount());
- verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt());
}
@Test
@@ -2245,7 +2240,7 @@
@Test
public void testBumpFGImportance_noChannelChangePreOApp() throws Exception {
- String preOPkg = "preO";
+ String preOPkg = PKG_N_MR1;
int preOUid = 145;
final ApplicationInfo legacy = new ApplicationInfo();
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
@@ -2335,7 +2330,7 @@
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
- NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 1, true);
+ final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 1, 2, true);
mService.mNotificationDelegate.onNotificationVisibilityChanged(
new NotificationVisibility[] {nv}, new NotificationVisibility[]{});
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
@@ -2349,8 +2344,9 @@
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
+ final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true);
mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.sbn.getTag(),
- r.sbn.getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD);
+ r.sbn.getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD, nv);
waitForIdle();
assertEquals(NotificationStats.DISMISSAL_AOD, r.getStats().getDismissalSurface());
@@ -2489,62 +2485,62 @@
}
@Test
- public void revokeUriPermissions_update() throws Exception {
+ public void updateUriPermissions_update() throws Exception {
NotificationChannel c = new NotificationChannel(
TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
c.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
Message message1 = new Message("", 0, "");
- message1.setData("", Uri.fromParts("old", "", "old stuff"));
+ message1.setData("",
+ ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1));
Message message2 = new Message("", 1, "");
- message2.setData("", Uri.fromParts("new", "", "new stuff"));
+ message2.setData("",
+ ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 2));
- Notification.Builder nb = new Notification.Builder(mContext, c.getId())
+ Notification.Builder nbA = new Notification.Builder(mContext, c.getId())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setStyle(new Notification.MessagingStyle("")
.addMessage(message1)
.addMessage(message2));
- StatusBarNotification oldSbn = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- NotificationRecord oldRecord =
- new NotificationRecord(mContext, oldSbn, c);
+ NotificationRecord recordA = new NotificationRecord(mContext, new StatusBarNotification(
+ PKG, PKG, 0, "tag", mUid, 0, nbA.build(), new UserHandle(mUid), null, 0), c);
- Notification.Builder nb1 = new Notification.Builder(mContext, c.getId())
+ // First post means we grant access to both
+ reset(mAm);
+ when(mAm.newUriPermissionOwner(any())).thenReturn(new Binder());
+ mService.updateUriPermissions(recordA, null, mContext.getPackageName(),
+ UserHandle.USER_SYSTEM);
+ verify(mAm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
+ eq(message1.getDataUri()), anyInt(), anyInt(), anyInt());
+ verify(mAm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
+ eq(message2.getDataUri()), anyInt(), anyInt(), anyInt());
+
+ Notification.Builder nbB = new Notification.Builder(mContext, c.getId())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setStyle(new Notification.MessagingStyle("").addMessage(message2));
- StatusBarNotification newSbn = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb1.build(), new UserHandle(mUid), null, 0);
- NotificationRecord newRecord =
- new NotificationRecord(mContext, newSbn, c);
+ NotificationRecord recordB = new NotificationRecord(mContext, new StatusBarNotification(PKG,
+ PKG, 0, "tag", mUid, 0, nbB.build(), new UserHandle(mUid), null, 0), c);
- mService.revokeUriPermissions(newRecord, oldRecord);
-
+ // Update means we drop access to first
+ reset(mAm);
+ mService.updateUriPermissions(recordB, recordA, mContext.getPackageName(),
+ UserHandle.USER_SYSTEM);
verify(mAm, times(1)).revokeUriPermissionFromOwner(any(), eq(message1.getDataUri()),
anyInt(), anyInt());
- }
- @Test
- public void revokeUriPermissions_cancel() throws Exception {
- NotificationChannel c = new NotificationChannel(
- TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
- c.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
- Message message1 = new Message("", 0, "");
- message1.setData("", Uri.fromParts("old", "", "old stuff"));
+ // Update back means we grant access to first again
+ reset(mAm);
+ mService.updateUriPermissions(recordA, recordB, mContext.getPackageName(),
+ UserHandle.USER_SYSTEM);
+ verify(mAm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
+ eq(message1.getDataUri()), anyInt(), anyInt(), anyInt());
- Notification.Builder nb = new Notification.Builder(mContext, c.getId())
- .setContentTitle("foo")
- .setSmallIcon(android.R.drawable.sym_def_app_icon)
- .setStyle(new Notification.MessagingStyle("")
- .addMessage(message1));
- StatusBarNotification oldSbn = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- NotificationRecord oldRecord =
- new NotificationRecord(mContext, oldSbn, c);
-
- mService.revokeUriPermissions(null, oldRecord);
-
- verify(mAm, times(1)).revokeUriPermissionFromOwner(any(), eq(message1.getDataUri()),
+ // And update to empty means we drop everything
+ reset(mAm);
+ mService.updateUriPermissions(null, recordB, mContext.getPackageName(),
+ UserHandle.USER_SYSTEM);
+ verify(mAm, times(1)).revokeUriPermissionFromOwner(any(), eq(null),
anyInt(), anyInt());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 6303184..e3289ab 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -29,8 +29,6 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -45,7 +43,6 @@
import android.media.AudioAttributes;
import android.metrics.LogMaker;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
@@ -54,7 +51,6 @@
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
-
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.UiServiceTestCase;
@@ -74,9 +70,9 @@
private final Context mMockContext = Mockito.mock(Context.class);
@Mock PackageManager mPm;
- private final String pkg = "com.android.server.notification";
+ private final String pkg = PKG_N_MR1;
private final int uid = 9583;
- private final String pkg2 = "pkg2";
+ private final String pkg2 = PKG_O;
private final int uid2 = 1111111;
private final int id1 = 1;
private final int id2 = 2;
@@ -119,13 +115,6 @@
when(mMockContext.getResources()).thenReturn(getContext().getResources());
when(mMockContext.getPackageManager()).thenReturn(mPm);
-
- legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
- upgrade.targetSdkVersion = Build.VERSION_CODES.O;
- try {
- when(mPm.getApplicationInfoAsUser(eq(pkg), anyInt(), anyInt())).thenReturn(legacy);
- when(mPm.getApplicationInfoAsUser(eq(pkg2), anyInt(), anyInt())).thenReturn(upgrade);
- } catch (PackageManager.NameNotFoundException e) {}
}
private StatusBarNotification getNotification(boolean preO, boolean noisy, boolean defaultSound,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index bda6b8a..8183a74 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -172,9 +172,13 @@
when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(SOUND_URI);
- mHelper = new RankingHelper(getContext(), mPm, mHandler, mock(ZenModeHelper.class),
+ ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
+ mHelper = new RankingHelper(getContext(), mPm, mHandler, mockZenModeHelper,
mUsageStats, new String[] {ImportanceExtractor.class.getName()});
+ when(mockZenModeHelper.getNotificationPolicy()).thenReturn(new NotificationManager.Policy(
+ 0, 0, 0));
+
mNotiGroupGSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentTitle("A")
.setGroup("G")
@@ -1129,6 +1133,50 @@
}
@Test
+ public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception {
+ // create notification channel that can't bypass dnd
+ // expected result: areChannelsBypassingDnd = false
+ NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ assertFalse(mHelper.areChannelsBypassingDnd());
+
+ // create notification channel that can bypass dnd
+ // expected result: areChannelsBypassingDnd = true
+ NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ channel2.setBypassDnd(true);
+ mHelper.createNotificationChannel(PKG, UID, channel2, true, true);
+ assertTrue(mHelper.areChannelsBypassingDnd());
+
+ // delete channels
+ mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+ assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
+ mHelper.deleteNotificationChannel(PKG, UID, channel2.getId());
+ assertFalse(mHelper.areChannelsBypassingDnd());
+
+ }
+
+ @Test
+ public void testUpdateCanChannelsBypassDnd() throws Exception {
+ // create notification channel that can't bypass dnd
+ // expected result: areChannelsBypassingDnd = false
+ NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ assertFalse(mHelper.areChannelsBypassingDnd());
+
+ // update channel so it CAN bypass dnd:
+ // expected result: areChannelsBypassingDnd = true
+ channel.setBypassDnd(true);
+ mHelper.updateNotificationChannel(PKG, UID, channel, true);
+ assertTrue(mHelper.areChannelsBypassingDnd());
+
+ // update channel so it can't bypass dnd:
+ // expected result: areChannelsBypassingDnd = false
+ channel.setBypassDnd(false);
+ mHelper.updateNotificationChannel(PKG, UID, channel, true);
+ assertFalse(mHelper.areChannelsBypassingDnd());
+ }
+
+ @Test
public void testCreateDeletedChannel() throws Exception {
long[] vibration = new long[]{100, 67, 145, 156};
NotificationChannel channel =
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 5e2a364..d49ba3e 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -71,7 +71,6 @@
@Before
public void setup() {
- LocalServices.addService(PackageManagerInternal.class, mock(PackageManagerInternal.class));
LocalServices.addService(UsageStatsManagerInternal.class,
mock(UsageStatsManagerInternal.class));
mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
@@ -85,7 +84,6 @@
@After
public void teardown() {
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
}
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 5f01518..920a605 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -63,6 +63,10 @@
import android.content.pm.ParceledListSlice;
import android.database.ContentObserver;
import android.hardware.display.DisplayManager;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
import android.net.NetworkScoreManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -191,6 +195,7 @@
long mCheckIdleIntervalMillis;
long mAppIdleParoleIntervalMillis;
+ long mAppIdleParoleWindowMillis;
long mAppIdleParoleDurationMillis;
long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS;
@@ -227,6 +232,7 @@
// TODO: Provide a mechanism to set an external bucketing service
private AppWidgetManager mAppWidgetManager;
+ private ConnectivityManager mConnectivityManager;
private PowerManager mPowerManager;
private PackageManager mPackageManager;
Injector mInjector;
@@ -326,6 +332,7 @@
settingsObserver.updateSettings();
mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
+ mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
mPowerManager = mContext.getSystemService(PowerManager.class);
mInjector.registerDisplayListener(mDisplayListener, mHandler);
@@ -414,7 +421,7 @@
postParoleEndTimeout();
} else {
mLastAppIdleParoledTime = now;
- postNextParoleTimeout(now);
+ postNextParoleTimeout(now, false);
}
postParoleStateChanged();
}
@@ -428,13 +435,18 @@
}
}
- private void postNextParoleTimeout(long now) {
+ private void postNextParoleTimeout(long now, boolean forced) {
if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
// Compute when the next parole needs to happen. We check more frequently than necessary
// since the message handler delays are based on elapsedRealTime and not wallclock time.
// The comparison is done in wallclock time.
long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now;
+ if (forced) {
+ // Set next timeout for the end of the parole window
+ // If parole is not set by the end of the window it will be forced
+ timeLeft += mAppIdleParoleWindowMillis;
+ }
if (timeLeft < 0) {
timeLeft = 0;
}
@@ -653,23 +665,49 @@
return THRESHOLD_BUCKETS[bucketIndex];
}
- /** Check if it's been a while since last parole and let idle apps do some work */
+ /**
+ * Check if it's been a while since last parole and let idle apps do some work.
+ * If network is not available, delay parole until it is available up until the end of the
+ * parole window. Force the parole to be set if end of the parole window is reached.
+ */
void checkParoleTimeout() {
boolean setParoled = false;
+ boolean waitForNetwork = false;
+ NetworkInfo activeNetwork = mConnectivityManager.getActiveNetworkInfo();
+ boolean networkActive = activeNetwork != null &&
+ activeNetwork.isConnected();
+
synchronized (mAppIdleLock) {
final long now = mInjector.currentTimeMillis();
if (!mAppIdleTempParoled) {
final long timeSinceLastParole = now - mLastAppIdleParoledTime;
if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
- setParoled = true;
+ if (networkActive) {
+ // If network is active set parole
+ setParoled = true;
+ } else {
+ if (timeSinceLastParole
+ > mAppIdleParoleIntervalMillis + mAppIdleParoleWindowMillis) {
+ if (DEBUG) Slog.d(TAG, "Crossed end of parole window, force parole");
+ setParoled = true;
+ } else {
+ if (DEBUG) Slog.d(TAG, "Network unavailable, delaying parole");
+ waitForNetwork = true;
+ postNextParoleTimeout(now, true);
+ }
+ }
} else {
if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
- postNextParoleTimeout(now);
+ postNextParoleTimeout(now, false);
}
}
}
+ if (waitForNetwork) {
+ mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
+ }
if (setParoled) {
+ // Set parole if network is available
setAppIdleParoled(true);
}
}
@@ -1321,6 +1359,10 @@
TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw);
pw.println();
+ pw.print(" mAppIdleParoleWindowMillis=");
+ TimeUtils.formatDuration(mAppIdleParoleWindowMillis, pw);
+ pw.println();
+
pw.print(" mAppIdleParoleDurationMillis=");
TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
pw.println();
@@ -1537,6 +1579,17 @@
}
}
+ private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder().build();
+
+ private final ConnectivityManager.NetworkCallback mNetworkCallback
+ = new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ mConnectivityManager.unregisterNetworkCallback(this);
+ checkParoleTimeout();
+ }
+ };
+
private final DisplayManager.DisplayListener mDisplayListener
= new DisplayManager.DisplayListener() {
@@ -1569,6 +1622,7 @@
private static final String KEY_IDLE_DURATION = "idle_duration2";
private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
private static final String KEY_PAROLE_INTERVAL = "parole_interval";
+ private static final String KEY_PAROLE_WINDOW = "parole_window";
private static final String KEY_PAROLE_DURATION = "parole_duration";
private static final String KEY_SCREEN_TIME_THRESHOLDS = "screen_thresholds";
private static final String KEY_ELAPSED_TIME_THRESHOLDS = "elapsed_thresholds";
@@ -1635,6 +1689,10 @@
mAppIdleParoleIntervalMillis = mParser.getDurationMillis(KEY_PAROLE_INTERVAL,
COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
+ // Default: 2 hours to wait on network
+ mAppIdleParoleWindowMillis = mParser.getDurationMillis(KEY_PAROLE_WINDOW,
+ COMPRESS_TIME ? ONE_MINUTE * 2 : 2 * 60 * ONE_MINUTE);
+
mAppIdleParoleDurationMillis = mParser.getDurationMillis(KEY_PAROLE_DURATION,
COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index f9fa336..f777f1d 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -62,6 +62,7 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -112,6 +113,7 @@
UserManager mUserManager;
PackageManager mPackageManager;
PackageManagerInternal mPackageManagerInternal;
+ PackageMonitor mPackageMonitor;
IDeviceIdleController mDeviceIdleController;
DevicePolicyManagerInternal mDpmInternal;
@@ -843,14 +845,19 @@
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
+ final int packageUid = mPackageManagerInternal.getPackageUid(packageName,
+ PackageManager.MATCH_ANY_USER, userId);
// If the calling app is asking about itself, continue, else check for permission.
- if (mPackageManagerInternal.getPackageUid(packageName, PackageManager.MATCH_ANY_USER,
- userId) != callingUid) {
+ if (packageUid != callingUid) {
if (!hasPermission(callingPackage)) {
throw new SecurityException(
"Don't have permission to query app standby bucket");
}
}
+ if (packageUid < 0) {
+ throw new IllegalArgumentException(
+ "Cannot get standby bucket for non existent package (" + packageName + ")");
+ }
final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(callingUid,
userId);
final long token = Binder.clearCallingIdentity();
@@ -886,11 +893,17 @@
: UsageStatsManager.REASON_MAIN_PREDICTED;
final long token = Binder.clearCallingIdentity();
try {
+ final int packageUid = mPackageManagerInternal.getPackageUid(packageName,
+ PackageManager.MATCH_ANY_USER, userId);
// Caller cannot set their own standby state
- if (mPackageManagerInternal.getPackageUid(packageName,
- PackageManager.MATCH_ANY_USER, userId) == callingUid) {
+ if (packageUid == callingUid) {
throw new IllegalArgumentException("Cannot set your own standby bucket");
}
+ if (packageUid < 0) {
+ throw new IllegalArgumentException(
+ "Cannot set standby bucket for non existent package (" + packageName
+ + ")");
+ }
mAppStandby.setAppStandbyBucket(packageName, userId, bucket, reason,
SystemClock.elapsedRealtime());
} finally {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a6ece89..1cc2595 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1948,6 +1948,15 @@
public static final String KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING =
"wcdma_default_signal_strength_measurement_string";
+ /**
+ * When a partial sms / mms message stay in raw table for too long without being completed,
+ * we expire them and delete them from the raw table. This carrier config defines the
+ * expiration time.
+ * @hide
+ */
+ public static final String KEY_UNDELIVERED_SMS_MESSAGE_EXPIRATION_TIME =
+ "undelivered_sms_message_expiration_time";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java
index 9726569..38678a3 100644
--- a/telephony/java/android/telephony/NetworkScanRequest.java
+++ b/telephony/java/android/telephony/NetworkScanRequest.java
@@ -152,7 +152,7 @@
this.mMaxSearchTime = maxSearchTime;
this.mIncrementalResults = incrementalResults;
this.mIncrementalResultsPeriodicity = incrementalResultsPeriodicity;
- if (mMccMncs != null) {
+ if (mccMncs != null) {
this.mMccMncs = (ArrayList<String>) mccMncs.clone();
} else {
this.mMccMncs = new ArrayList<>();
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 0ff2982..c16701b 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -204,6 +204,16 @@
public static final int LISTEN_VOLTE_STATE = 0x00004000;
/**
+ * Listen for OEM hook raw event
+ *
+ * @see #onOemHookRawEvent
+ * @hide
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
+ */
+ @Deprecated
+ public static final int LISTEN_OEM_HOOK_RAW_EVENT = 0x00008000;
+
+ /**
* Listen for carrier network changes indicated by a carrier app.
*
* @see #onCarrierNetworkRequest
@@ -367,6 +377,9 @@
case LISTEN_USER_MOBILE_DATA_STATE:
PhoneStateListener.this.onUserMobileDataStateChanged((boolean)msg.obj);
break;
+ case LISTEN_OEM_HOOK_RAW_EVENT:
+ PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj);
+ break;
case LISTEN_CARRIER_NETWORK_CHANGE:
PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
break;
@@ -583,6 +596,16 @@
}
/**
+ * Callback invoked when OEM hook raw event is received. Requires
+ * the READ_PRIVILEGED_PHONE_STATE permission.
+ * @param rawData is the byte array of the OEM hook raw data.
+ * @hide
+ */
+ public void onOemHookRawEvent(byte[] rawData) {
+ // default implementation empty
+ }
+
+ /**
* Callback invoked when telephony has received notice from a carrier
* app that a network action that could result in connectivity loss
* has been requested by an app using
@@ -698,6 +721,10 @@
send(LISTEN_USER_MOBILE_DATA_STATE, 0, 0, enabled);
}
+ public void onOemHookRawEvent(byte[] rawData) {
+ send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData);
+ }
+
public void onCarrierNetworkChange(boolean active) {
send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ca8c6d6..ba39ffd 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6445,6 +6445,29 @@
return retVal;
}
+ /**
+ * Returns the result and response from RIL for oem request
+ *
+ * @param oemReq the data is sent to ril.
+ * @param oemResp the respose data from RIL.
+ * @return negative value request was not handled or get error
+ * 0 request was handled succesfully, but no response data
+ * positive value success, data length of response
+ * @hide
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
+ */
+ @Deprecated
+ public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.invokeOemRilRequestRaw(oemReq, oemResp);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return -1;
+ }
+
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 1cfe8c2..0d315e5 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -47,6 +47,7 @@
void onVoLteServiceStateChanged(in VoLteServiceState lteState);
void onVoiceActivationStateChanged(int activationState);
void onDataActivationStateChanged(int activationState);
+ void onOemHookRawEvent(in byte[] rawData);
void onCarrierNetworkChange(in boolean active);
void onUserMobileDataStateChanged(in boolean enabled);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 7e8b2de..73cd498 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1071,6 +1071,17 @@
in List<String> cdmaNonRoamingList);
/**
+ * Returns the result and response from RIL for oem request
+ *
+ * @param oemReq the data is sent to ril.
+ * @param oemResp the respose data from RIL.
+ * @return negative value request was not handled or get error
+ * 0 request was handled succesfully, but no response data
+ * positive value success, data length of response
+ */
+ int invokeOemRilRequestRaw(in byte[] oemReq, out byte[] oemResp);
+
+ /**
* Check if any mobile Radios need to be shutdown.
*
* @return true is any mobile radio needs to be shutdown
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 06dc13e..0127db9 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -70,6 +70,7 @@
void notifyVoLteServiceStateChanged(in VoLteServiceState lteState);
void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
int activationState, int activationType);
+ void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
void notifySubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index dd56e0e..4ca175f 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -98,7 +98,7 @@
private static final String LAUNCH_FILE = "applaunch.txt";
private static final String TRACE_SUB_DIRECTORY = "atrace_logs";
private static final String DEFAULT_TRACE_CATEGORIES =
- "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm";
+ "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm,binder_driver,hal";
private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000";
private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10";
private static final String TRIAL_LAUNCH = "TRIAL_LAUNCH";
@@ -310,7 +310,8 @@
try {
atraceLogger.atraceStart(traceCategoriesSet, traceBufferSize,
traceDumpInterval, rootTraceSubDir,
- String.format("%s-%s", launch.getApp(), launch.getLaunchReason()));
+ String.format("%s-%s-%s", launch.getApp(),
+ launch.getCompilerFilter(), launch.getLaunchReason()));
startApp(launch.getApp(), launch.getLaunchReason());
sleep(POST_LAUNCH_IDLE_TIMEOUT);
} finally {
diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java
index f3c22a5..9695e9a 100644
--- a/tests/net/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/java/android/net/LinkPropertiesTest.java
@@ -27,6 +27,7 @@
import android.net.LinkProperties.CompareResult;
import android.net.LinkProperties.ProvisioningChange;
import android.net.RouteInfo;
+import android.os.Parcel;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.system.OsConstants;
@@ -82,6 +83,9 @@
assertTrue(source.isIdenticalPrivateDns(target));
assertTrue(target.isIdenticalPrivateDns(source));
+ assertTrue(source.isIdenticalValidatedPrivateDnses(target));
+ assertTrue(target.isIdenticalValidatedPrivateDnses(source));
+
assertTrue(source.isIdenticalRoutes(target));
assertTrue(target.isIdenticalRoutes(source));
@@ -784,4 +788,35 @@
assertEquals(new ArraySet<>(expectAdded), new ArraySet<>(result.added));
assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed)));
}
+
+ @Test
+ public void testLinkPropertiesParcelable() {
+ LinkProperties source = new LinkProperties();
+ source.setInterfaceName(NAME);
+ // set 2 link addresses
+ source.addLinkAddress(LINKADDRV4);
+ source.addLinkAddress(LINKADDRV6);
+ // set 2 dnses
+ source.addDnsServer(DNS1);
+ source.addDnsServer(DNS2);
+ // set 2 gateways
+ source.addRoute(new RouteInfo(GATEWAY1));
+ source.addRoute(new RouteInfo(GATEWAY2));
+ // set 2 validated private dnses
+ source.addValidatedPrivateDnsServer(DNS6);
+ source.addValidatedPrivateDnsServer(GATEWAY61);
+
+ source.setMtu(MTU);
+
+ Parcel p = Parcel.obtain();
+ source.writeToParcel(p, /* flags */ 0);
+ p.setDataPosition(0);
+ final byte[] marshalled = p.marshall();
+ p = Parcel.obtain();
+ p.unmarshall(marshalled, 0, marshalled.length);
+ p.setDataPosition(0);
+ LinkProperties dest = LinkProperties.CREATOR.createFromParcel(p);
+
+ assertEquals(source, dest);
+ }
}
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 9364ec8..f8a4132 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -30,7 +30,6 @@
import android.content.Context;
import android.net.LinkAddress;
import android.net.LinkProperties;
-import android.net.NetworkUtils;
import android.net.apf.ApfFilter.ApfConfiguration;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
@@ -42,22 +41,13 @@
import android.os.Parcelable;
import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.system.ErrnoException;
import android.system.Os;
import android.text.format.DateUtils;
-
import com.android.frameworks.tests.net.R;
import com.android.internal.util.HexDump;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
@@ -68,9 +58,14 @@
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Random;
-
import libcore.io.IoUtils;
import libcore.io.Streams;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
/**
* Tests for APF program generator and interpreter.
@@ -82,6 +77,7 @@
@SmallTest
public class ApfTest {
private static final int TIMEOUT_MS = 500;
+ private final static int MIN_APF_VERSION = 2;
@Mock IpConnectivityLog mLog;
@Mock Context mContext;
@@ -131,11 +127,11 @@
}
private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) {
- assertReturnCodesEqual(expected, apfSimulate(program, packet, filterAge));
+ assertReturnCodesEqual(expected, apfSimulate(program, packet, null, filterAge));
}
private void assertVerdict(int expected, byte[] program, byte[] packet) {
- assertReturnCodesEqual(expected, apfSimulate(program, packet, 0));
+ assertReturnCodesEqual(expected, apfSimulate(program, packet, null, 0));
}
private void assertPass(byte[] program, byte[] packet, int filterAge) {
@@ -154,9 +150,24 @@
assertVerdict(DROP, program, packet);
}
+ private void assertDataMemoryContents(
+ int expected, byte[] program, byte[] packet, byte[] data, byte[] expected_data)
+ throws IllegalInstructionException, Exception {
+ assertReturnCodesEqual(expected, apfSimulate(program, packet, data, 0 /* filterAge */));
+
+ // assertArrayEquals() would only print one byte, making debugging difficult.
+ if (!java.util.Arrays.equals(expected_data, data)) {
+ throw new Exception(
+ "program: " + HexDump.toHexString(program) +
+ "\ndata memory: " + HexDump.toHexString(data) +
+ "\nexpected: " + HexDump.toHexString(expected_data));
+ }
+ }
+
private void assertVerdict(int expected, ApfGenerator gen, byte[] packet, int filterAge)
throws IllegalInstructionException {
- assertReturnCodesEqual(expected, apfSimulate(gen.generate(), packet, filterAge));
+ assertReturnCodesEqual(expected, apfSimulate(gen.generate(), packet, null,
+ filterAge));
}
private void assertPass(ApfGenerator gen, byte[] packet, int filterAge)
@@ -189,11 +200,11 @@
// Empty program should pass because having the program counter reach the
// location immediately after the program indicates the packet should be
// passed to the AP.
- ApfGenerator gen = new ApfGenerator();
+ ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION);
assertPass(gen);
// Test jumping to pass label.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJump(gen.PASS_LABEL);
byte[] program = gen.generate();
assertEquals(1, program.length);
@@ -201,7 +212,7 @@
assertPass(program, new byte[MIN_PKT_SIZE], 0);
// Test jumping to drop label.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJump(gen.DROP_LABEL);
program = gen.generate();
assertEquals(2, program.length);
@@ -210,121 +221,121 @@
assertDrop(program, new byte[15], 15);
// Test jumping if equal to 0.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
assertDrop(gen);
// Test jumping if not equal to 0.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL);
assertDrop(gen);
// Test jumping if registers equal.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0EqualsR1(gen.DROP_LABEL);
assertDrop(gen);
// Test jumping if registers not equal.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
assertDrop(gen);
// Test load immediate.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test add.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addAdd(1234567890);
gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test subtract.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addAdd(-1234567890);
gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test or.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addOr(1234567890);
gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test and.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addAnd(123456789);
gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL);
assertDrop(gen);
// Test left shift.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addLeftShift(1);
gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL);
assertDrop(gen);
// Test right shift.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addRightShift(1);
gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL);
assertDrop(gen);
// Test multiply.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addMul(2);
gen.addJumpIfR0Equals(1234567890 * 2, gen.DROP_LABEL);
assertDrop(gen);
// Test divide.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addDiv(2);
gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL);
assertDrop(gen);
// Test divide by zero.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addDiv(0);
gen.addJump(gen.DROP_LABEL);
assertPass(gen);
// Test add.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 1234567890);
gen.addAddR1();
gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test subtract.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, -1234567890);
gen.addAddR1();
gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test or.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 1234567890);
gen.addOrR1();
gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test and.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addLoadImmediate(Register.R1, 123456789);
gen.addAndR1();
@@ -332,7 +343,7 @@
assertDrop(gen);
// Test left shift.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addLoadImmediate(Register.R1, 1);
gen.addLeftShiftR1();
@@ -340,7 +351,7 @@
assertDrop(gen);
// Test right shift.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addLoadImmediate(Register.R1, -1);
gen.addLeftShiftR1();
@@ -348,7 +359,7 @@
assertDrop(gen);
// Test multiply.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addLoadImmediate(Register.R1, 2);
gen.addMulR1();
@@ -356,7 +367,7 @@
assertDrop(gen);
// Test divide.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addLoadImmediate(Register.R1, 2);
gen.addDivR1();
@@ -364,136 +375,136 @@
assertDrop(gen);
// Test divide by zero.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addDivR1();
gen.addJump(gen.DROP_LABEL);
assertPass(gen);
// Test byte load.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoad8(Register.R0, 1);
gen.addJumpIfR0Equals(45, gen.DROP_LABEL);
assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
// Test out of bounds load.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoad8(Register.R0, 16);
gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
// Test half-word load.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoad16(Register.R0, 1);
gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL);
assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
// Test word load.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoad32(Register.R0, 1);
gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL);
assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0);
// Test byte indexed load.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 1);
gen.addLoad8Indexed(Register.R0, 0);
gen.addJumpIfR0Equals(45, gen.DROP_LABEL);
assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
// Test out of bounds indexed load.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 8);
gen.addLoad8Indexed(Register.R0, 8);
gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
// Test half-word indexed load.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 1);
gen.addLoad16Indexed(Register.R0, 0);
gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL);
assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
// Test word indexed load.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 1);
gen.addLoad32Indexed(Register.R0, 0);
gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL);
assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0);
// Test jumping if greater than.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL);
assertDrop(gen);
// Test jumping if less than.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0LessThan(0, gen.DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0LessThan(1, gen.DROP_LABEL);
assertDrop(gen);
// Test jumping if any bits set.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
assertDrop(gen);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 3);
gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
assertDrop(gen);
// Test jumping if register greater than.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 2);
gen.addLoadImmediate(Register.R1, 1);
gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL);
assertDrop(gen);
// Test jumping if register less than.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0LessThanR1(gen.DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 1);
gen.addJumpIfR0LessThanR1(gen.DROP_LABEL);
assertDrop(gen);
// Test jumping if any bits set in register.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 3);
gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
assertPass(gen);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 3);
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
assertDrop(gen);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 3);
gen.addLoadImmediate(Register.R0, 3);
gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
assertDrop(gen);
// Test load from memory.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadFromMemory(Register.R0, 0);
gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
assertDrop(gen);
// Test store to memory.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 1234567890);
gen.addStoreToMemory(Register.R1, 12);
gen.addLoadFromMemory(Register.R0, 12);
@@ -501,63 +512,63 @@
assertDrop(gen);
// Test filter age pre-filled memory.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT);
gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
assertDrop(gen, new byte[MIN_PKT_SIZE], 1234567890);
// Test packet size pre-filled memory.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
gen.addJumpIfR0Equals(MIN_PKT_SIZE, gen.DROP_LABEL);
assertDrop(gen);
// Test IPv4 header size pre-filled memory.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
gen.addJumpIfR0Equals(20, gen.DROP_LABEL);
assertDrop(gen, new byte[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x45}, 0);
// Test not.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addNot(Register.R0);
gen.addJumpIfR0Equals(~1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test negate.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addNeg(Register.R0);
gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test move.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 1234567890);
gen.addMove(Register.R0);
gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
assertDrop(gen);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addMove(Register.R1);
gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test swap.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 1234567890);
gen.addSwap();
gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
assertDrop(gen);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addSwap();
gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
assertDrop(gen);
// Test jump if bytes not equal.
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
program = gen.generate();
@@ -569,25 +580,160 @@
assertEquals(1, program[4]);
assertEquals(123, program[5]);
assertDrop(program, new byte[MIN_PKT_SIZE], 0);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
byte[] packet123 = {0,123,0,0,0,0,0,0,0,0,0,0,0,0,0};
assertPass(gen, packet123, 0);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
assertDrop(gen, packet123, 0);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,30,4,5}, gen.DROP_LABEL);
byte[] packet12345 = {0,1,2,3,4,5,0,0,0,0,0,0,0,0,0};
assertDrop(gen, packet12345, 0);
- gen = new ApfGenerator();
+ gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,3,4,5}, gen.DROP_LABEL);
assertPass(gen, packet12345, 0);
}
+ @Test(expected = ApfGenerator.IllegalInstructionException.class)
+ public void testApfGeneratorWantsV2OrGreater() throws Exception {
+ // The minimum supported APF version is 2.
+ new ApfGenerator(1);
+ }
+
+ @Test
+ public void testApfDataOpcodesWantApfV3() throws IllegalInstructionException, Exception {
+ ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION);
+ try {
+ gen.addStoreData(Register.R0, 0);
+ fail();
+ } catch (IllegalInstructionException expected) {
+ /* pass */
+ }
+ try {
+ gen.addLoadData(Register.R0, 0);
+ fail();
+ } catch (IllegalInstructionException expected) {
+ /* pass */
+ }
+ }
+
+ @Test
+ public void testApfDataWrite() throws IllegalInstructionException, Exception {
+ byte[] packet = new byte[MIN_PKT_SIZE];
+ byte[] data = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+ byte[] expected_data = data.clone();
+
+ // No memory access instructions: should leave the data segment untouched.
+ ApfGenerator gen = new ApfGenerator(3);
+ assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
+
+ // Expect value 0x87654321 to be stored starting from address -11 from the end of the
+ // data buffer, in big-endian order.
+ gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R0, 0x87654321);
+ gen.addLoadImmediate(Register.R1, -5);
+ gen.addStoreData(Register.R0, -6); // -5 + -6 = -11 (offset +5 with data_len=16)
+ expected_data[5] = (byte)0x87;
+ expected_data[6] = (byte)0x65;
+ expected_data[7] = (byte)0x43;
+ expected_data[8] = (byte)0x21;
+ assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
+ }
+
+ @Test
+ public void testApfDataRead() throws IllegalInstructionException, Exception {
+ // Program that DROPs if address 10 (-6) contains 0x87654321.
+ ApfGenerator gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R1, 10);
+ gen.addLoadData(Register.R0, -16); // 10 + -16 = -6 (offset +10 with data_len=16)
+ gen.addJumpIfR0Equals(0x87654321, gen.DROP_LABEL);
+ byte[] program = gen.generate();
+ byte[] packet = new byte[MIN_PKT_SIZE];
+
+ // Content is incorrect (last byte does not match) -> PASS
+ byte[] data = new byte[16];
+ data[10] = (byte)0x87;
+ data[11] = (byte)0x65;
+ data[12] = (byte)0x43;
+ data[13] = (byte)0x00; // != 0x21
+ byte[] expected_data = data.clone();
+ assertDataMemoryContents(PASS, program, packet, data, expected_data);
+
+ // Fix the last byte -> conditional jump taken -> DROP
+ data[13] = (byte)0x21;
+ expected_data = data;
+ assertDataMemoryContents(DROP, program, packet, data, expected_data);
+ }
+
+ @Test
+ public void testApfDataReadModifyWrite() throws IllegalInstructionException, Exception {
+ ApfGenerator gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R1, -22);
+ gen.addLoadData(Register.R0, 0); // Load from address 32 -22 + 0 = 10
+ gen.addAdd(0x78453412); // 87654321 + 78453412 = FFAA7733
+ gen.addStoreData(Register.R0, 4); // Write back to address 32 -22 + 4 = 14
+
+ byte[] packet = new byte[MIN_PKT_SIZE];
+ byte[] data = new byte[32];
+ data[10] = (byte)0x87;
+ data[11] = (byte)0x65;
+ data[12] = (byte)0x43;
+ data[13] = (byte)0x21;
+ byte[] expected_data = data.clone();
+ expected_data[14] = (byte)0xFF;
+ expected_data[15] = (byte)0xAA;
+ expected_data[16] = (byte)0x77;
+ expected_data[17] = (byte)0x33;
+ assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
+ }
+
+ @Test
+ public void testApfDataBoundChecking() throws IllegalInstructionException, Exception {
+ byte[] packet = new byte[MIN_PKT_SIZE];
+ byte[] data = new byte[32];
+ byte[] expected_data = data;
+
+ // Program that DROPs unconditionally. This is our the baseline.
+ ApfGenerator gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R0, 3);
+ gen.addLoadData(Register.R1, 7);
+ gen.addJump(gen.DROP_LABEL);
+ assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
+
+ // Same program as before, but this time we're trying to load past the end of the data.
+ gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R0, 20);
+ gen.addLoadData(Register.R1, 15); // 20 + 15 > 32
+ gen.addJump(gen.DROP_LABEL); // Not reached.
+ assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
+
+ // Subtracting an immediate should work...
+ gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R0, 20);
+ gen.addLoadData(Register.R1, -4);
+ gen.addJump(gen.DROP_LABEL);
+ assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
+
+ // ...and underflowing simply wraps around to the end of the buffer...
+ gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R0, 20);
+ gen.addLoadData(Register.R1, -30);
+ gen.addJump(gen.DROP_LABEL);
+ assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
+
+ // ...but doesn't allow accesses before the start of the buffer
+ gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R0, 20);
+ gen.addLoadData(Register.R1, -1000);
+ gen.addJump(gen.DROP_LABEL); // Not reached.
+ assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
+ }
+
/**
* Generate some BPF programs, translate them to APF, then run APF and BPF programs
* over packet traces and verify both programs filter out the same packets.
@@ -1422,10 +1568,11 @@
}
/**
- * Call the APF interpreter the run {@code program} on {@code packet} pretending the
- * filter was installed {@code filter_age} seconds ago.
+ * Call the APF interpreter to run {@code program} on {@code packet} with persistent memory
+ * segment {@data} pretending the filter was installed {@code filter_age} seconds ago.
*/
- private native static int apfSimulate(byte[] program, byte[] packet, int filter_age);
+ private native static int apfSimulate(byte[] program, byte[] packet, byte[] data,
+ int filter_age);
/**
* Compile a tcpdump human-readable filter (e.g. "icmp" or "tcp port 54") into a BPF
diff --git a/tests/net/java/android/net/apf/Bpf2Apf.java b/tests/net/java/android/net/apf/Bpf2Apf.java
index 220e54d..5d57cde 100644
--- a/tests/net/java/android/net/apf/Bpf2Apf.java
+++ b/tests/net/java/android/net/apf/Bpf2Apf.java
@@ -307,7 +307,7 @@
* program and return it.
*/
public static byte[] convert(String bpf) throws IllegalInstructionException {
- ApfGenerator gen = new ApfGenerator();
+ ApfGenerator gen = new ApfGenerator(3);
for (String line : bpf.split("\\n")) convertLine(line, gen);
return gen.generate();
}
@@ -320,7 +320,7 @@
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String line = null;
StringBuilder responseData = new StringBuilder();
- ApfGenerator gen = new ApfGenerator();
+ ApfGenerator gen = new ApfGenerator(3);
while ((line = in.readLine()) != null) convertLine(line, gen);
System.out.write(gen.generate());
}
diff --git a/tests/net/jni/apf_jni.cpp b/tests/net/jni/apf_jni.cpp
index 152e6c3..1ea9e27 100644
--- a/tests/net/jni/apf_jni.cpp
+++ b/tests/net/jni/apf_jni.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2016, The Android Open Source Project
+ * Copyright 2018, 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.
@@ -28,15 +28,31 @@
// JNI function acting as simply call-through to native APF interpreter.
static jint com_android_server_ApfTest_apfSimulate(
- JNIEnv* env, jclass, jbyteArray program, jbyteArray packet, jint filter_age) {
- return accept_packet(
- (uint8_t*)env->GetByteArrayElements(program, NULL),
- env->GetArrayLength(program),
- (uint8_t*)env->GetByteArrayElements(packet, NULL),
- env->GetArrayLength(packet),
- nullptr,
- 0,
- filter_age);
+ JNIEnv* env, jclass, jbyteArray program, jbyteArray packet,
+ jbyteArray data, jint filter_age) {
+ uint8_t* program_raw = (uint8_t*)env->GetByteArrayElements(program, nullptr);
+ uint8_t* packet_raw = (uint8_t*)env->GetByteArrayElements(packet, nullptr);
+ uint8_t* data_raw = (uint8_t*)(data ? env->GetByteArrayElements(data, nullptr) : nullptr);
+ uint32_t program_len = env->GetArrayLength(program);
+ uint32_t packet_len = env->GetArrayLength(packet);
+ uint32_t data_len = data ? env->GetArrayLength(data) : 0;
+
+ // Merge program and data into a single buffer.
+ uint8_t* program_and_data = (uint8_t*)malloc(program_len + data_len);
+ memcpy(program_and_data, program_raw, program_len);
+ memcpy(program_and_data + program_len, data_raw, data_len);
+
+ jint result =
+ accept_packet(program_and_data, program_len, program_len + data_len,
+ packet_raw, packet_len, filter_age);
+ if (data) {
+ memcpy(data_raw, program_and_data + program_len, data_len);
+ env->ReleaseByteArrayElements(data, (jbyte*)data_raw, 0 /* copy back */);
+ }
+ free(program_and_data);
+ env->ReleaseByteArrayElements(packet, (jbyte*)packet_raw, JNI_ABORT);
+ env->ReleaseByteArrayElements(program, (jbyte*)program_raw, JNI_ABORT);
+ return result;
}
class ScopedPcap {
@@ -102,8 +118,8 @@
jstring jpcap_filename, jbyteArray japf_program) {
ScopedUtfChars filter(env, jfilter);
ScopedUtfChars pcap_filename(env, jpcap_filename);
- const uint8_t* apf_program = (uint8_t*)env->GetByteArrayElements(japf_program, NULL);
- const uint32_t apf_program_len = env->GetArrayLength(japf_program);
+ uint8_t* apf_program = (uint8_t*)env->GetByteArrayElements(japf_program, NULL);
+ uint32_t apf_program_len = env->GetArrayLength(japf_program);
// Open pcap file for BPF filtering
ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb"));
@@ -145,8 +161,8 @@
do {
apf_packet = pcap_next(apf_pcap.get(), &apf_header);
} while (apf_packet != NULL && !accept_packet(
- apf_program, apf_program_len, apf_packet, apf_header.len,
- nullptr, 0, 0));
+ apf_program, apf_program_len, 0 /* data_len */,
+ apf_packet, apf_header.len, 0 /* filter_age */));
// Make sure both filters matched the same packet.
if (apf_packet == NULL && bpf_packet == NULL)
@@ -170,7 +186,7 @@
}
static JNINativeMethod gMethods[] = {
- { "apfSimulate", "([B[BI)I",
+ { "apfSimulate", "([B[B[BI)I",
(void*)com_android_server_ApfTest_apfSimulate },
{ "compileToBpf", "(Ljava/lang/String;)Ljava/lang/String;",
(void*)com_android_server_ApfTest_compileToBpf },
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 1b6f882..19c6c31 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -586,7 +586,29 @@
out_resource->name.type = ResourceType::kId;
out_resource->name.entry = maybe_name.value().to_string();
- out_resource->value = util::make_unique<Id>();
+
+ // Ids either represent a unique resource id or reference another resource id
+ auto item = ParseItem(parser, out_resource, resource_format);
+ if (!item) {
+ return false;
+ }
+
+ String* empty = ValueCast<String>(out_resource->value.get());
+ if (empty && *empty->value == "") {
+ // If no inner element exists, represent a unique identifier
+ out_resource->value = util::make_unique<Id>();
+ } else {
+ // If an inner element exists, the inner element must be a reference to
+ // another resource id
+ Reference* ref = ValueCast<Reference>(out_resource->value.get());
+ if (!ref || ref->name.value().type != ResourceType::kId) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<" << parser->element_name()
+ << "> inner element must either be a resource reference or empty");
+ return false;
+ }
+ }
+
return true;
}
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index fc1aeaa..c12b9fa 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -933,4 +933,32 @@
EXPECT_FALSE(TestParse(input));
}
+TEST_F(ResourceParserTest, ParseIdItem) {
+ std::string input = R"(
+ <item name="foo" type="id">@id/bar</item>
+ <item name="bar" type="id"/>
+ <item name="baz" type="id"></item>)";
+ ASSERT_TRUE(TestParse(input));
+
+ ASSERT_THAT(test::GetValue<Reference>(&table_, "id/foo"), NotNull());
+ ASSERT_THAT(test::GetValue<Id>(&table_, "id/bar"), NotNull());
+ ASSERT_THAT(test::GetValue<Id>(&table_, "id/baz"), NotNull());
+
+ // Reject attribute references
+ input = R"(<item name="foo2" type="id">?attr/bar"</item>)";
+ ASSERT_FALSE(TestParse(input));
+
+ // Reject non-references
+ input = R"(<item name="foo3" type="id">0x7f010001</item>)";
+ ASSERT_FALSE(TestParse(input));
+ input = R"(<item name="foo4" type="id">@drawable/my_image</item>)";
+ ASSERT_FALSE(TestParse(input));
+ input = R"(<item name="foo5" type="id"><string name="biz"></string></item>)";
+ ASSERT_FALSE(TestParse(input));
+
+ // Ids that reference other resource ids cannot be public
+ input = R"(<public name="foo6" type="id">@id/bar6</item>)";
+ ASSERT_FALSE(TestParse(input));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index b0ce9e1..b37e1fb 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -172,9 +172,11 @@
StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, const Context& context,
bool unique) {
if (unique) {
- auto iter = indexed_strings_.find(str);
- if (iter != std::end(indexed_strings_)) {
- return Ref(iter->second);
+ auto range = indexed_strings_.equal_range(str);
+ for (auto iter = range.first; iter != range.second; ++iter) {
+ if (context.priority == iter->second->context.priority) {
+ return Ref(iter->second);
+ }
}
}
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 58a03de..4b3afe2 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -61,6 +61,17 @@
EXPECT_THAT(pool.size(), Eq(1u));
}
+TEST(StringPoolTest, DoNotDedupeSameStringDifferentPriority) {
+ StringPool pool;
+
+ StringPool::Ref ref_a = pool.MakeRef("wut", StringPool::Context(0x81010001));
+ StringPool::Ref ref_b = pool.MakeRef("wut", StringPool::Context(0x81010002));
+
+ EXPECT_THAT(*ref_a, Eq("wut"));
+ EXPECT_THAT(*ref_b, Eq("wut"));
+ EXPECT_THAT(pool.size(), Eq(2u));
+}
+
TEST(StringPoolTest, MaintainInsertionOrderIndex) {
StringPool pool;