summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PREUPLOAD.cfg4
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.cpp44
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.h12
-rwxr-xr-xcmds/statsd/src/socket/StatsSocketListener.cpp3
-rw-r--r--config/boot-image-profile.txt134
-rw-r--r--config/hiddenapi-light-greylist.txt56
-rw-r--r--core/java/android/content/res/ResourcesImpl.java7
-rw-r--r--core/java/android/net/IpConfiguration.java2
-rw-r--r--core/java/android/net/NetworkUtils.java40
-rw-r--r--core/java/android/os/ZygoteProcess.java3
-rw-r--r--core/java/android/util/FeatureFlagUtils.java1
-rw-r--r--core/java/android/widget/RemoteViews.java66
-rw-r--r--core/java/com/android/internal/content/FileSystemProvider.java8
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java20
-rw-r--r--core/java/com/android/internal/os/Zygote.java2
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java4
-rw-r--r--core/jni/android_net_NetUtils.cpp50
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp54
-rw-r--r--core/res/res/values/config.xml7
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java108
-rw-r--r--keystore/java/android/security/keystore/AttestationUtils.java35
-rw-r--r--media/java/android/media/MediaCodec.java2
-rw-r--r--packages/PackageInstaller/res/drawable/ic_file_download.xml3
-rw-r--r--packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml2
-rw-r--r--packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml2
-rw-r--r--packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml2
-rw-r--r--packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java20
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java94
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java1
-rw-r--r--packages/SystemUI/Android.bp54
-rw-r--r--packages/SystemUI/docs/kotlin-in-sysui.md22
-rw-r--r--packages/SystemUI/res/values/dimens.xml1
-rw-r--r--packages/SystemUI/res/values/strings.xml9
-rw-r--r--packages/SystemUI/res/values/styles.xml5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/BatteryMeterView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/ImageWallpaper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/OverviewProxyService.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.java128
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java153
-rw-r--r--packages/SystemUI/tests/Android.mk18
-rw-r--r--packages/SystemUI/tests/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt60
-rw-r--r--services/core/java/com/android/server/AppOpsService.java54
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java8
-rw-r--r--services/core/java/com/android/server/am/BackupRecord.java7
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java7
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java13
-rw-r--r--services/net/java/android/net/dhcp/DhcpDiscoverPacket.java15
-rw-r--r--services/net/java/android/net/dhcp/DhcpLease.java138
-rw-r--r--services/net/java/android/net/dhcp/DhcpLeaseRepository.java538
-rw-r--r--services/net/java/android/net/dhcp/DhcpNakPacket.java16
-rw-r--r--services/net/java/android/net/dhcp/DhcpPacket.java102
-rw-r--r--services/net/java/android/net/dhcp/DhcpPacketListener.java84
-rw-r--r--services/net/java/android/net/dhcp/DhcpReleasePacket.java58
-rw-r--r--services/net/java/android/net/dhcp/DhcpRequestPacket.java6
-rw-r--r--services/net/java/android/net/dhcp/DhcpServingParams.java262
-rw-r--r--services/net/java/android/net/ip/IpClient.java2
-rw-r--r--services/net/java/android/net/util/FdEventsReader.java254
-rw-r--r--services/net/java/android/net/util/NetworkConstants.java4
-rw-r--r--services/net/java/android/net/util/PacketReader.java210
-rw-r--r--services/net/java/android/net/util/SharedLog.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java5
-rw-r--r--telephony/java/android/telephony/CellIdentity.java6
-rw-r--r--telephony/java/android/telephony/CellIdentityCdma.java14
-rw-r--r--telephony/java/android/telephony/CellIdentityGsm.java12
-rw-r--r--telephony/java/android/telephony/CellIdentityLte.java23
-rw-r--r--telephony/java/android/telephony/CellIdentityTdscdma.java12
-rw-r--r--telephony/java/android/telephony/CellIdentityWcdma.java14
-rw-r--r--tests/net/java/android/net/NetworkUtilsTest.java43
-rw-r--r--tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java519
-rw-r--r--tests/net/java/android/net/dhcp/DhcpServingParamsTest.java177
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp4
-rwxr-xr-xtools/aosp/aosp_sha.sh18
-rw-r--r--tools/stats_log_api_gen/main.cpp13
94 files changed, 3306 insertions, 871 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index ae42882f917a..de83f3e01074 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -15,3 +15,7 @@ api_lint_hook = ${REPO_ROOT}/frameworks/base/tools/apilint/apilint_sha.sh ${PREU
strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
hidden_api_txt_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+
+ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
+
+owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index c37d0cfdba93..33f3917f05ad 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -50,7 +50,7 @@ const int FIELD_ID_ANOMALY_ALARM_STATS = 9;
// const int FIELD_ID_PULLED_ATOM_STATS = 10; // The proto is written in stats_log_util.cpp
const int FIELD_ID_LOGGER_ERROR_STATS = 11;
const int FIELD_ID_PERIODIC_ALARM_STATS = 12;
-const int FIELD_ID_LOG_LOSS_STATS = 14;
+// const int FIELD_ID_LOG_LOSS_STATS = 14;
const int FIELD_ID_SYSTEM_SERVER_RESTART = 15;
const int FIELD_ID_ATOM_STATS_TAG = 1;
@@ -180,12 +180,12 @@ void StatsdStats::noteConfigReset(const ConfigKey& key) {
noteConfigResetInternalLocked(key);
}
-void StatsdStats::noteLogLost(int64_t timestampNs, int32_t count) {
+void StatsdStats::noteLogLost(int32_t wallClockTimeSec, int32_t count) {
lock_guard<std::mutex> lock(mLock);
- if (mLogLossTimestampNs.size() == kMaxLoggerErrors) {
- mLogLossTimestampNs.pop_front();
+ if (mLogLossStats.size() == kMaxLoggerErrors) {
+ mLogLossStats.pop_front();
}
- mLogLossTimestampNs.push_back(std::make_pair(timestampNs, count));
+ mLogLossStats.push_back(std::make_pair(wallClockTimeSec, count));
}
void StatsdStats::noteBroadcastSent(const ConfigKey& key) {
@@ -365,15 +365,6 @@ void StatsdStats::noteSystemServerRestart(int32_t timeSec) {
mSystemServerRestartSec.push_back(timeSec);
}
-void StatsdStats::noteLoggerError(int error) {
- lock_guard<std::mutex> lock(mLock);
- // grows strictly one at a time. so it won't > kMaxLoggerErrors
- if (mLoggerErrors.size() == kMaxLoggerErrors) {
- mLoggerErrors.pop_front();
- }
- mLoggerErrors.push_back(std::make_pair(getWallClockSec(), error));
-}
-
void StatsdStats::reset() {
lock_guard<std::mutex> lock(mLock);
resetInternalLocked();
@@ -386,9 +377,8 @@ void StatsdStats::resetInternalLocked() {
std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0);
mAnomalyAlarmRegisteredStats = 0;
mPeriodicAlarmRegisteredStats = 0;
- mLoggerErrors.clear();
mSystemServerRestartSec.clear();
- mLogLossTimestampNs.clear();
+ mLogLossStats.clear();
for (auto& config : mConfigStats) {
config.second->broadcast_sent_time_sec.clear();
config.second->data_drop_time_sec.clear();
@@ -515,21 +505,13 @@ void StatsdStats::dumpStats(FILE* out) const {
mUidMapStats.bytes_used, mUidMapStats.changes, mUidMapStats.deleted_apps,
mUidMapStats.dropped_changes);
- for (const auto& error : mLoggerErrors) {
- time_t error_time = error.first;
- struct tm* error_tm = localtime(&error_time);
- char buffer[80];
- strftime(buffer, sizeof(buffer), "%Y-%m-%d %I:%M%p\n", error_tm);
- fprintf(out, "Logger error %d at %s\n", error.second, buffer);
- }
-
for (const auto& restart : mSystemServerRestartSec) {
fprintf(out, "System server restarts at %s(%lld)\n",
buildTimeString(restart).c_str(), (long long)restart);
}
- for (const auto& loss : mLogLossTimestampNs) {
- fprintf(out, "Log loss: %lld (elapsedRealtimeNs) - %d (count)\n", (long long)loss.first,
+ for (const auto& loss : mLogLossStats) {
+ fprintf(out, "Log loss: %lld (wall clock sec) - %d (count)\n", (long long)loss.first,
loss.second);
}
}
@@ -678,7 +660,10 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DELETED_APPS, mUidMapStats.deleted_apps);
proto.end(uidMapToken);
- for (const auto& error : mLoggerErrors) {
+ for (const auto& error : mLogLossStats) {
+ // The logger error stats are not used anymore since we move away from logd.
+ // Temporarily use this field to log the log loss timestamp and count
+ // TODO(b/80538532) Add a dedicated field in stats_log for this.
uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_LOGGER_ERROR_STATS |
FIELD_COUNT_REPEATED);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOGGER_STATS_TIME, error.first);
@@ -686,11 +671,6 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
proto.end(token);
}
- for (const auto& loss : mLogLossTimestampNs) {
- proto.write(FIELD_TYPE_INT64 | FIELD_ID_LOG_LOSS_STATS | FIELD_COUNT_REPEATED,
- (long long)loss.first);
- }
-
for (const auto& restart : mSystemServerRestartSec) {
proto.write(FIELD_TYPE_INT32 | FIELD_ID_SYSTEM_SERVER_RESTART | FIELD_COUNT_REPEATED,
restart);
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index daea027e68f9..b5156dadade6 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -271,11 +271,6 @@ public:
// Notify pull request for an atom served from cached data
void notePullFromCache(int pullAtomId);
- /**
- * Records statsd met an error while reading from logd.
- */
- void noteLoggerError(int error);
-
/*
* Records when system server restarts.
*/
@@ -284,7 +279,7 @@ public:
/**
* Records statsd skipped an event.
*/
- void noteLogLost(int64_t timestamp, int32_t count);
+ void noteLogLost(int32_t wallClockTimeSec, int32_t count);
/**
* Reset the historical stats. Including all stats in icebox, and the tracked stats about
@@ -338,11 +333,8 @@ private:
// Maps PullAtomId to its stats. The size is capped by the puller atom counts.
std::map<int, PulledAtomStats> mPulledAtomStats;
- // Logd errors. Size capped by kMaxLoggerErrors.
- std::list<const std::pair<int, int>> mLoggerErrors;
-
// Timestamps when we detect log loss, and the number of logs lost.
- std::list<std::pair<int64_t, int32_t>> mLogLossTimestampNs;
+ std::list<std::pair<int32_t, int32_t>> mLogLossStats;
std::list<int32_t> mSystemServerRestartSec;
diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp
index 4041da7b84d2..9b0691b6092d 100755
--- a/cmds/statsd/src/socket/StatsSocketListener.cpp
+++ b/cmds/statsd/src/socket/StatsSocketListener.cpp
@@ -111,7 +111,8 @@ bool StatsSocketListener::onDataAvailable(SocketClient* cli) {
android_log_event_int_t* int_event = reinterpret_cast<android_log_event_int_t*>(ptr);
if (int_event->header.tag == kLibLogTag && int_event->payload.type == EVENT_TYPE_INT) {
ALOGE("Found dropped events: %d", int_event->payload.data);
- StatsdStats::getInstance().noteLogLost(getElapsedRealtimeNs(), int_event->payload.data);
+ StatsdStats::getInstance().noteLogLost((int32_t)getWallClockSec(),
+ int_event->payload.data);
return true;
}
}
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index a78773692ef3..9ca0745c04f4 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -12204,11 +12204,11 @@ HPLorg/json/JSONStringer;->value(J)Lorg/json/JSONStringer;
HPLorg/json/JSONStringer;->value(Z)Lorg/json/JSONStringer;
HPLorg/json/JSONTokener;->syntaxError(Ljava/lang/String;)Lorg/json/JSONException;
HPLorg/json/JSONTokener;->toString()Ljava/lang/String;
-HPLorg/kxml2/io/KXmlSerializer;->getDepth()I
-HPLorg/kxml2/io/KXmlSerializer;->getNamespace()Ljava/lang/String;
-HPLorg/kxml2/io/KXmlSerializer;->getPrefix(Ljava/lang/String;ZZ)Ljava/lang/String;
-HPLorg/kxml2/io/KXmlSerializer;->setPrefix(Ljava/lang/String;Ljava/lang/String;)V
-HPLorg/kxml2/io/KXmlSerializer;->text(Ljava/lang/String;)Lorg/xmlpull/v1/XmlSerializer;
+HPLcom/android/org/kxml2/io/KXmlSerializer;->getDepth()I
+HPLcom/android/org/kxml2/io/KXmlSerializer;->getNamespace()Ljava/lang/String;
+HPLcom/android/org/kxml2/io/KXmlSerializer;->getPrefix(Ljava/lang/String;ZZ)Ljava/lang/String;
+HPLcom/android/org/kxml2/io/KXmlSerializer;->setPrefix(Ljava/lang/String;Ljava/lang/String;)V
+HPLcom/android/org/kxml2/io/KXmlSerializer;->text(Ljava/lang/String;)Lorg/xmlpull/v1/XmlSerializer;
HPLorg/w3c/dom/NamedNodeMap;->getLength()I
HPLorg/w3c/dom/NamedNodeMap;->getNamedItem(Ljava/lang/String;)Lorg/w3c/dom/Node;
HPLorg/w3c/dom/NamedNodeMap;->getNamedItemNS(Ljava/lang/String;Ljava/lang/String;)Lorg/w3c/dom/Node;
@@ -51896,7 +51896,7 @@ HSPLorg/apache/harmony/xml/dom/NodeListImpl;->item(I)Lorg/w3c/dom/Node;
HSPLorg/apache/harmony/xml/dom/TextImpl;->getNodeType()S
HSPLorg/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl;->newDocumentBuilder()Ljavax/xml/parsers/DocumentBuilder;
HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;->appendText(Lorg/apache/harmony/xml/dom/DocumentImpl;Lorg/w3c/dom/Node;ILjava/lang/String;)V
-HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;->parse(Lorg/kxml2/io/KXmlParser;Lorg/apache/harmony/xml/dom/DocumentImpl;Lorg/w3c/dom/Node;I)V
+HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;->parse(Lcom/android/org/kxml2/io/KXmlParser;Lorg/apache/harmony/xml/dom/DocumentImpl;Lorg/w3c/dom/Node;I)V
HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;->parse(Lorg/xml/sax/InputSource;)Lorg/w3c/dom/Document;
HSPLorg/apache/harmony/xml/parsers/SAXParserFactoryImpl;->getFeature(Ljava/lang/String;)Z
HSPLorg/apache/harmony/xml/parsers/SAXParserFactoryImpl;->isValidating()Z
@@ -52176,64 +52176,64 @@ HSPLorg/json/JSONTokener;->readEscapeCharacter()C
HSPLorg/json/JSONTokener;->readLiteral()Ljava/lang/Object;
HSPLorg/json/JSONTokener;->readObject()Lorg/json/JSONObject;
HSPLorg/json/JSONTokener;->skipToEndOfLine()V
-HSPLorg/kxml2/io/KXmlParser$ValueContext;-><init>(Ljava/lang/String;I)V
-HSPLorg/kxml2/io/KXmlParser;-><init>()V
-HSPLorg/kxml2/io/KXmlParser;->adjustNsp()Z
-HSPLorg/kxml2/io/KXmlParser;->close()V
-HSPLorg/kxml2/io/KXmlParser;->fillBuffer(I)Z
-HSPLorg/kxml2/io/KXmlParser;->getAttributeCount()I
-HSPLorg/kxml2/io/KXmlParser;->getAttributeName(I)Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->getAttributeNamespace(I)Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->getAttributePrefix(I)Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->getAttributeType(I)Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->getAttributeValue(I)Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->getAttributeValue(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->getColumnNumber()I
-HSPLorg/kxml2/io/KXmlParser;->getDepth()I
-HSPLorg/kxml2/io/KXmlParser;->getEventType()I
-HSPLorg/kxml2/io/KXmlParser;->getFeature(Ljava/lang/String;)Z
-HSPLorg/kxml2/io/KXmlParser;->getLineNumber()I
-HSPLorg/kxml2/io/KXmlParser;->getName()Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->getNamespace()Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->getNamespace(Ljava/lang/String;)Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->getNamespaceCount(I)I
-HSPLorg/kxml2/io/KXmlParser;->getPositionDescription()Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->getPrefix()Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->getText()Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->getTextCharacters([I)[C
-HSPLorg/kxml2/io/KXmlParser;->next()I
-HSPLorg/kxml2/io/KXmlParser;->next(Z)I
-HSPLorg/kxml2/io/KXmlParser;->nextTag()I
-HSPLorg/kxml2/io/KXmlParser;->nextText()Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->nextToken()I
-HSPLorg/kxml2/io/KXmlParser;->parseStartTag(ZZ)V
-HSPLorg/kxml2/io/KXmlParser;->peekType(Z)I
-HSPLorg/kxml2/io/KXmlParser;->read(C)V
-HSPLorg/kxml2/io/KXmlParser;->read([C)V
-HSPLorg/kxml2/io/KXmlParser;->readComment(Z)Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->readEndTag()V
-HSPLorg/kxml2/io/KXmlParser;->readEntity(Ljava/lang/StringBuilder;ZZLorg/kxml2/io/KXmlParser$ValueContext;)V
-HSPLorg/kxml2/io/KXmlParser;->readName()Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->readUntil([CZ)Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->readValue(CZZLorg/kxml2/io/KXmlParser$ValueContext;)Ljava/lang/String;
-HSPLorg/kxml2/io/KXmlParser;->readXmlDeclaration()V
-HSPLorg/kxml2/io/KXmlParser;->require(ILjava/lang/String;Ljava/lang/String;)V
-HSPLorg/kxml2/io/KXmlParser;->setFeature(Ljava/lang/String;Z)V
-HSPLorg/kxml2/io/KXmlParser;->setInput(Ljava/io/InputStream;Ljava/lang/String;)V
-HSPLorg/kxml2/io/KXmlParser;->setInput(Ljava/io/Reader;)V
-HSPLorg/kxml2/io/KXmlSerializer;-><init>()V
-HSPLorg/kxml2/io/KXmlSerializer;->append(Ljava/lang/String;II)V
-HSPLorg/kxml2/io/KXmlSerializer;->attribute(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lorg/xmlpull/v1/XmlSerializer;
-HSPLorg/kxml2/io/KXmlSerializer;->check(Z)V
-HSPLorg/kxml2/io/KXmlSerializer;->endDocument()V
-HSPLorg/kxml2/io/KXmlSerializer;->endTag(Ljava/lang/String;Ljava/lang/String;)Lorg/xmlpull/v1/XmlSerializer;
-HSPLorg/kxml2/io/KXmlSerializer;->flush()V
-HSPLorg/kxml2/io/KXmlSerializer;->setFeature(Ljava/lang/String;Z)V
-HSPLorg/kxml2/io/KXmlSerializer;->setOutput(Ljava/io/OutputStream;Ljava/lang/String;)V
-HSPLorg/kxml2/io/KXmlSerializer;->setOutput(Ljava/io/Writer;)V
-HSPLorg/kxml2/io/KXmlSerializer;->startDocument(Ljava/lang/String;Ljava/lang/Boolean;)V
-HSPLorg/kxml2/io/KXmlSerializer;->startTag(Ljava/lang/String;Ljava/lang/String;)Lorg/xmlpull/v1/XmlSerializer;
-HSPLorg/kxml2/io/KXmlSerializer;->writeEscaped(Ljava/lang/String;I)V
+HSPLcom/android/org/kxml2/io/KXmlParser$ValueContext;-><init>(Ljava/lang/String;I)V
+HSPLcom/android/org/kxml2/io/KXmlParser;-><init>()V
+HSPLcom/android/org/kxml2/io/KXmlParser;->adjustNsp()Z
+HSPLcom/android/org/kxml2/io/KXmlParser;->close()V
+HSPLcom/android/org/kxml2/io/KXmlParser;->fillBuffer(I)Z
+HSPLcom/android/org/kxml2/io/KXmlParser;->getAttributeCount()I
+HSPLcom/android/org/kxml2/io/KXmlParser;->getAttributeName(I)Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->getAttributeNamespace(I)Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->getAttributePrefix(I)Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->getAttributeType(I)Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->getAttributeValue(I)Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->getAttributeValue(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->getColumnNumber()I
+HSPLcom/android/org/kxml2/io/KXmlParser;->getDepth()I
+HSPLcom/android/org/kxml2/io/KXmlParser;->getEventType()I
+HSPLcom/android/org/kxml2/io/KXmlParser;->getFeature(Ljava/lang/String;)Z
+HSPLcom/android/org/kxml2/io/KXmlParser;->getLineNumber()I
+HSPLcom/android/org/kxml2/io/KXmlParser;->getName()Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->getNamespace()Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->getNamespace(Ljava/lang/String;)Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->getNamespaceCount(I)I
+HSPLcom/android/org/kxml2/io/KXmlParser;->getPositionDescription()Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->getPrefix()Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->getText()Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->getTextCharacters([I)[C
+HSPLcom/android/org/kxml2/io/KXmlParser;->next()I
+HSPLcom/android/org/kxml2/io/KXmlParser;->next(Z)I
+HSPLcom/android/org/kxml2/io/KXmlParser;->nextTag()I
+HSPLcom/android/org/kxml2/io/KXmlParser;->nextText()Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->nextToken()I
+HSPLcom/android/org/kxml2/io/KXmlParser;->parseStartTag(ZZ)V
+HSPLcom/android/org/kxml2/io/KXmlParser;->peekType(Z)I
+HSPLcom/android/org/kxml2/io/KXmlParser;->read(C)V
+HSPLcom/android/org/kxml2/io/KXmlParser;->read([C)V
+HSPLcom/android/org/kxml2/io/KXmlParser;->readComment(Z)Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->readEndTag()V
+HSPLcom/android/org/kxml2/io/KXmlParser;->readEntity(Ljava/lang/StringBuilder;ZZLcom/android/org/kxml2/io/KXmlParser$ValueContext;)V
+HSPLcom/android/org/kxml2/io/KXmlParser;->readName()Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->readUntil([CZ)Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->readValue(CZZLcom/android/org/kxml2/io/KXmlParser$ValueContext;)Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlParser;->readXmlDeclaration()V
+HSPLcom/android/org/kxml2/io/KXmlParser;->require(ILjava/lang/String;Ljava/lang/String;)V
+HSPLcom/android/org/kxml2/io/KXmlParser;->setFeature(Ljava/lang/String;Z)V
+HSPLcom/android/org/kxml2/io/KXmlParser;->setInput(Ljava/io/InputStream;Ljava/lang/String;)V
+HSPLcom/android/org/kxml2/io/KXmlParser;->setInput(Ljava/io/Reader;)V
+HSPLcom/android/org/kxml2/io/KXmlSerializer;-><init>()V
+HSPLcom/android/org/kxml2/io/KXmlSerializer;->append(Ljava/lang/String;II)V
+HSPLcom/android/org/kxml2/io/KXmlSerializer;->attribute(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lorg/xmlpull/v1/XmlSerializer;
+HSPLcom/android/org/kxml2/io/KXmlSerializer;->check(Z)V
+HSPLcom/android/org/kxml2/io/KXmlSerializer;->endDocument()V
+HSPLcom/android/org/kxml2/io/KXmlSerializer;->endTag(Ljava/lang/String;Ljava/lang/String;)Lorg/xmlpull/v1/XmlSerializer;
+HSPLcom/android/org/kxml2/io/KXmlSerializer;->flush()V
+HSPLcom/android/org/kxml2/io/KXmlSerializer;->setFeature(Ljava/lang/String;Z)V
+HSPLcom/android/org/kxml2/io/KXmlSerializer;->setOutput(Ljava/io/OutputStream;Ljava/lang/String;)V
+HSPLcom/android/org/kxml2/io/KXmlSerializer;->setOutput(Ljava/io/Writer;)V
+HSPLcom/android/org/kxml2/io/KXmlSerializer;->startDocument(Ljava/lang/String;Ljava/lang/Boolean;)V
+HSPLcom/android/org/kxml2/io/KXmlSerializer;->startTag(Ljava/lang/String;Ljava/lang/String;)Lorg/xmlpull/v1/XmlSerializer;
+HSPLcom/android/org/kxml2/io/KXmlSerializer;->writeEscaped(Ljava/lang/String;I)V
HSPLorg/w3c/dom/Attr;->getName()Ljava/lang/String;
HSPLorg/w3c/dom/Attr;->getOwnerElement()Lorg/w3c/dom/Element;
HSPLorg/w3c/dom/Attr;->getSchemaTypeInfo()Lorg/w3c/dom/TypeInfo;
@@ -63397,9 +63397,9 @@ Lorg/json/JSONObject;
Lorg/json/JSONStringer$Scope;
Lorg/json/JSONStringer;
Lorg/json/JSONTokener;
-Lorg/kxml2/io/KXmlParser$ValueContext;
-Lorg/kxml2/io/KXmlParser;
-Lorg/kxml2/io/KXmlSerializer;
+Lcom/android/org/kxml2/io/KXmlParser$ValueContext;
+Lcom/android/org/kxml2/io/KXmlParser;
+Lcom/android/org/kxml2/io/KXmlSerializer;
Lorg/w3c/dom/CharacterData;
Lorg/w3c/dom/DOMImplementation;
Lorg/w3c/dom/Document;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 3a96cd3f3eca..196f89cd69ee 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -455,59 +455,6 @@ Landroid/database/IContentObserver$Stub;->asInterface(Landroid/os/IBinder;)Landr
Landroid/database/IContentObserver;->onChange(ZLandroid/net/Uri;I)V
Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_VENDOR_BASE:I
Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_VENDOR_BASE:I
-Landroid/hardware/camera2/CameraCharacteristics;->CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->CONTROL_MAX_REGIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->LED_AVAILABLE_LEDS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->LENS_INFO_SHADING_MAP_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->LOGICAL_MULTI_CAMERA_PHYSICAL_IDS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->QUIRKS_USE_PARTIAL_RESULT:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_CHARACTERISTICS_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_REQUEST_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_RESULT_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_SESSION_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_MAX_NUM_OUTPUT_STREAMS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_FORMATS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_JPEG_MIN_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_JPEG_SIZES:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_PROCESSED_SIZES:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CaptureRequest;->JPEG_GPS_COORDINATES:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->JPEG_GPS_PROCESSING_METHOD:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->JPEG_GPS_TIMESTAMP:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->LED_TRANSMIT:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->REQUEST_ID:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->TONEMAP_CURVE_BLUE:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->TONEMAP_CURVE_GREEN:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->TONEMAP_CURVE_RED:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureResult;->JPEG_GPS_COORDINATES:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->JPEG_GPS_PROCESSING_METHOD:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->JPEG_GPS_TIMESTAMP:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->LED_TRANSMIT:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->QUIRKS_PARTIAL_RESULT:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->REQUEST_FRAME_COUNT:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->REQUEST_ID:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_FACE_IDS:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_FACE_LANDMARKS:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_FACE_RECTANGLES:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_FACE_SCORES:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_LENS_SHADING_MAP:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_TIMESTAMPS:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_X_SHIFTS:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_Y_SHIFTS:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_PREDICTED_COLOR_GAINS:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_PREDICTED_COLOR_TRANSFORM:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->SYNC_FRAME_NUMBER:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_BLUE:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_GREEN:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_RED:Landroid/hardware/camera2/CaptureResult$Key;
Landroid/hardware/display/IDisplayManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/display/IDisplayManager;
Landroid/hardware/display/IDisplayManager;->getDisplayInfo(I)Landroid/view/DisplayInfo;
Landroid/hardware/fingerprint/IFingerprintService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -2480,7 +2427,6 @@ Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landr
Lcom/android/internal/util/HexDump;->toHexString([BZ)Ljava/lang/String;
Lcom/android/internal/view/BaseIWindow;-><init>()V
Lcom/android/internal/view/IInputMethod$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethod;
-Lcom/android/internal/view/IInputMethod;->attachToken(Landroid/os/IBinder;)V
Lcom/android/internal/view/IInputMethod;->bindInput(Landroid/view/inputmethod/InputBinding;)V
Lcom/android/internal/view/IInputMethod;->hideSoftInput(ILandroid/os/ResultReceiver;)V
Lcom/android/internal/view/IInputMethod;->setSessionEnabled(Lcom/android/internal/view/IInputMethodSession;Z)V
@@ -2492,8 +2438,6 @@ Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;-><init>(Landroid/os/I
Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;->getEnabledInputMethodList()Ljava/util/List;
Lcom/android/internal/view/IInputMethodManager$Stub;-><init>()V
Lcom/android/internal/view/IInputMethodManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodManager;
-Lcom/android/internal/view/IInputMethodManager;->addClient(Lcom/android/internal/view/IInputMethodClient;Lcom/android/internal/view/IInputContext;II)V
-Lcom/android/internal/view/IInputMethodManager;->removeClient(Lcom/android/internal/view/IInputMethodClient;)V
Lcom/android/internal/view/IInputMethodSession$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodSession;
Lcom/android/internal/widget/ILockSettings$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/widget/ILockSettings;
Lcom/android/internal/widget/ILockSettings;->getBoolean(Ljava/lang/String;ZI)Z
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index b644e23223b7..dfa30a22597e 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -1349,9 +1349,11 @@ public class ResourcesImpl {
@StyleableRes int[] attrs,
@AttrRes int defStyleAttr,
@StyleRes int defStyleRes) {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "obtainStyledAttributes");
+ TypedArray array;
synchronized (mKey) {
final int len = attrs.length;
- final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
+ array = TypedArray.obtain(wrapper.getResources(), len);
// XXX note that for now we only work with compiled XML files.
// To support generic XML files we will need to manually parse
@@ -1362,8 +1364,9 @@ public class ResourcesImpl {
array.mDataAddress, array.mIndicesAddress);
array.mTheme = wrapper;
array.mXml = parser;
- return array;
}
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ return array;
}
@NonNull
diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java
index 7543920e363f..3319f33878c1 100644
--- a/core/java/android/net/IpConfiguration.java
+++ b/core/java/android/net/IpConfiguration.java
@@ -35,7 +35,7 @@ public class IpConfiguration implements Parcelable {
* with staticIpConfiguration */
@UnsupportedAppUsage
STATIC,
- /* Use dynamically configured IP settigns */
+ /* Use dynamically configured IP settings */
DHCP,
/* no IP details are assigned, this is used to indicate
* that any existing IP settings should be retained */
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 599ccb287a26..34e9476b3e08 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -22,6 +22,7 @@ import android.util.Log;
import android.util.Pair;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -131,6 +132,17 @@ public class NetworkUtils {
public native static boolean queryUserAccess(int uid, int netId);
/**
+ * Add an entry into the ARP cache.
+ */
+ public static void addArpEntry(Inet4Address ipv4Addr, MacAddress ethAddr, String ifname,
+ FileDescriptor fd) throws IOException {
+ addArpEntry(ethAddr.toByteArray(), ipv4Addr.getAddress(), ifname, fd);
+ }
+
+ private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname,
+ FileDescriptor fd) throws IOException;
+
+ /**
* @see #intToInet4AddressHTL(int)
* @deprecated Use either {@link #intToInet4AddressHTH(int)}
* or {@link #intToInet4AddressHTL(int)}
@@ -149,7 +161,7 @@ public class NetworkUtils {
* @param hostAddress an int coding for an IPv4 address, where higher-order int byte is
* lower-order IPv4 address byte
*/
- public static InetAddress intToInet4AddressHTL(int hostAddress) {
+ public static Inet4Address intToInet4AddressHTL(int hostAddress) {
return intToInet4AddressHTH(Integer.reverseBytes(hostAddress));
}
@@ -157,14 +169,14 @@ public class NetworkUtils {
* Convert a IPv4 address from an integer to an InetAddress (0x01020304 -> 1.2.3.4)
* @param hostAddress an int coding for an IPv4 address
*/
- public static InetAddress intToInet4AddressHTH(int hostAddress) {
+ public static Inet4Address intToInet4AddressHTH(int hostAddress) {
byte[] addressBytes = { (byte) (0xff & (hostAddress >> 24)),
(byte) (0xff & (hostAddress >> 16)),
(byte) (0xff & (hostAddress >> 8)),
(byte) (0xff & hostAddress) };
try {
- return InetAddress.getByAddress(addressBytes);
+ return (Inet4Address) InetAddress.getByAddress(addressBytes);
} catch (UnknownHostException e) {
throw new AssertionError();
}
@@ -397,6 +409,28 @@ public class NetworkUtils {
}
/**
+ * Get a prefix mask as Inet4Address for a given prefix length.
+ *
+ * <p>For example 20 -> 255.255.240.0
+ */
+ public static Inet4Address getPrefixMaskAsInet4Address(int prefixLength)
+ throws IllegalArgumentException {
+ return intToInet4AddressHTH(prefixLengthToV4NetmaskIntHTH(prefixLength));
+ }
+
+ /**
+ * Get the broadcast address for a given prefix.
+ *
+ * <p>For example 192.168.0.1/24 -> 192.168.0.255
+ */
+ public static Inet4Address getBroadcastAddress(Inet4Address addr, int prefixLength)
+ throws IllegalArgumentException {
+ final int intBroadcastAddr = inet4AddressToIntHTH(addr)
+ | ~prefixLengthToV4NetmaskIntHTH(prefixLength);
+ return intToInet4AddressHTH(intBroadcastAddr);
+ }
+
+ /**
* Check if IP address type is consistent between two InetAddress.
* @return true if both are the same type. False otherwise.
*/
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 067e8493717b..732d3778ec6d 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -384,7 +384,10 @@ public class ZygoteProcess {
argsForZygote.add("--mount-external-read");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
argsForZygote.add("--mount-external-write");
+ } else if (mountExternal == Zygote.MOUNT_EXTERNAL_FULL) {
+ argsForZygote.add("--mount-external-full");
}
+
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
// --setgroups is a comma-separated list
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index c037cd062b82..a7f14b6bf0c6 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -44,6 +44,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put("settings_dynamic_homepage", "false");
+ DEFAULT_FLAGS.put("settings_mobile_network_v2", "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "true");
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 8fa8ef88a3ad..35ff6cc23499 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -16,8 +16,6 @@
package android.widget;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
import android.annotation.ColorInt;
import android.annotation.DimenRes;
import android.annotation.NonNull;
@@ -364,29 +362,12 @@ public class RemoteViews implements Parcelable, Filter {
/** @hide */
public static class OnClickHandler {
- private int mEnterAnimationId;
-
@UnsupportedAppUsage
- public boolean onClickHandler(View view, PendingIntent pendingIntent,
- Intent fillInIntent) {
- return onClickHandler(view, pendingIntent, fillInIntent, WINDOWING_MODE_UNDEFINED);
- }
-
- public boolean onClickHandler(View view, PendingIntent pendingIntent,
- Intent fillInIntent, int windowingMode) {
+ public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) {
try {
// TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
Context context = view.getContext();
- ActivityOptions opts;
- if (mEnterAnimationId != 0) {
- opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0);
- } else {
- opts = ActivityOptions.makeBasic();
- }
-
- if (windowingMode != WINDOWING_MODE_UNDEFINED) {
- opts.setLaunchWindowingMode(windowingMode);
- }
+ ActivityOptions opts = getActivityOptions(context);
context.startIntentSender(
pendingIntent.getIntentSender(), fillInIntent,
Intent.FLAG_ACTIVITY_NEW_TASK,
@@ -402,8 +383,26 @@ public class RemoteViews implements Parcelable, Filter {
return true;
}
- public void setEnterAnimationId(int enterAnimationId) {
- mEnterAnimationId = enterAnimationId;
+ /** @hide */
+ protected ActivityOptions getActivityOptions(Context context) {
+ if (context.getResources().getBoolean(
+ com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
+ TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
+ com.android.internal.R.styleable.Window);
+ int windowAnimations = windowStyle.getResourceId(
+ com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
+ TypedArray windowAnimationStyle = context.obtainStyledAttributes(
+ windowAnimations, com.android.internal.R.styleable.WindowAnimation);
+ int enterAnimationId = windowAnimationStyle.getResourceId(com.android.internal.R
+ .styleable.WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0);
+ windowStyle.recycle();
+ windowAnimationStyle.recycle();
+
+ if (enterAnimationId != 0) {
+ return ActivityOptions.makeCustomAnimation(context, enterAnimationId, 0);
+ }
+ }
+ return ActivityOptions.makeBasic();
}
}
@@ -3324,8 +3323,6 @@ public class RemoteViews implements Parcelable, Filter {
RemoteViews rvToApply = getRemoteViewsToApply(context);
View result = inflateView(context, rvToApply, parent);
- loadTransitionOverride(context, handler);
-
rvToApply.performApply(result, parent, handler);
return result;
@@ -3355,24 +3352,6 @@ public class RemoteViews implements Parcelable, Filter {
return v;
}
- private static void loadTransitionOverride(Context context,
- RemoteViews.OnClickHandler handler) {
- if (handler != null && context.getResources().getBoolean(
- com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
- TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
- com.android.internal.R.styleable.Window);
- int windowAnimations = windowStyle.getResourceId(
- com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
- TypedArray windowAnimationStyle = context.obtainStyledAttributes(
- windowAnimations, com.android.internal.R.styleable.WindowAnimation);
- handler.setEnterAnimationId(windowAnimationStyle.getResourceId(
- com.android.internal.R.styleable.
- WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0));
- windowStyle.recycle();
- windowAnimationStyle.recycle();
- }
- }
-
/**
* Implement this interface to receive a callback when
* {@link #applyAsync} or {@link #reapplyAsync} is finished.
@@ -3445,7 +3424,6 @@ public class RemoteViews implements Parcelable, Filter {
mHandler = handler;
mResult = result;
- loadTransitionOverride(context, handler);
}
@Override
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index f89a9d990b76..0c6f832ff33a 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -374,8 +374,12 @@ public abstract class FileSystemProvider extends DocumentsProvider {
final File parent = getFileForDocId(parentDocumentId);
final MatrixCursor result = new DirectoryCursor(
resolveProjection(projection), parentDocumentId, parent);
- for (File file : parent.listFiles()) {
- includeFile(result, null, file);
+ if (parent.isDirectory()) {
+ for (File file : FileUtils.listFilesOrEmpty(parent)) {
+ includeFile(result, null, file);
+ }
+ } else {
+ Log.w(TAG, "parentDocumentId '" + parentDocumentId + "' is not Directory");
}
return result;
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index c65dd6fc1bb2..07f74f0506b2 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -6724,7 +6724,7 @@ public class BatteryStatsImpl extends BatteryStats {
int mWifiBatchedScanBinStarted = NO_BATCHED_SCAN_STARTED;
StopwatchTimer[] mWifiBatchedScanTimer;
- boolean mWifiMulticastEnabled;
+ int mWifiMulticastWakelockCount;
StopwatchTimer mWifiMulticastTimer;
StopwatchTimer mAudioTurnedOnTimer;
@@ -7183,8 +7183,7 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
public void noteWifiMulticastEnabledLocked(long elapsedRealtimeMs) {
- if (!mWifiMulticastEnabled) {
- mWifiMulticastEnabled = true;
+ if (mWifiMulticastWakelockCount == 0) {
if (mWifiMulticastTimer == null) {
mWifiMulticastTimer = new StopwatchTimer(mBsi.mClocks, Uid.this,
WIFI_MULTICAST_ENABLED, mBsi.mWifiMulticastTimers, mBsi.mOnBatteryTimeBase);
@@ -7194,12 +7193,17 @@ public class BatteryStatsImpl extends BatteryStats {
StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__ON);
}
+ mWifiMulticastWakelockCount++;
}
@Override
public void noteWifiMulticastDisabledLocked(long elapsedRealtimeMs) {
- if (mWifiMulticastEnabled) {
- mWifiMulticastEnabled = false;
+ if (mWifiMulticastWakelockCount == 0) {
+ return;
+ }
+
+ mWifiMulticastWakelockCount--;
+ if (mWifiMulticastWakelockCount == 0) {
mWifiMulticastTimer.stopRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(
StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
@@ -7932,7 +7936,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (mWifiMulticastTimer != null) {
active |= !mWifiMulticastTimer.reset(false);
- active |= mWifiMulticastEnabled;
+ active |= (mWifiMulticastWakelockCount > 0);
}
active |= !resetIfNotNull(mAudioTurnedOnTimer, false);
@@ -8590,7 +8594,7 @@ public class BatteryStatsImpl extends BatteryStats {
mWifiBatchedScanTimer[i] = null;
}
}
- mWifiMulticastEnabled = false;
+ mWifiMulticastWakelockCount = 0;
if (in.readInt() != 0) {
mWifiMulticastTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, WIFI_MULTICAST_ENABLED,
mBsi.mWifiMulticastTimers, mBsi.mOnBatteryTimeBase, in);
@@ -14031,7 +14035,7 @@ public class BatteryStatsImpl extends BatteryStats {
u.mWifiBatchedScanTimer[i].readSummaryFromParcelLocked(in);
}
}
- u.mWifiMulticastEnabled = false;
+ u.mWifiMulticastWakelockCount = 0;
if (in.readInt() != 0) {
u.mWifiMulticastTimer.readSummaryFromParcelLocked(in);
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 413f89da571e..927322e97e28 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -81,6 +81,8 @@ public final class Zygote {
public static final int MOUNT_EXTERNAL_READ = IVold.REMOUNT_MODE_READ;
/** Read-write external storage should be mounted. */
public static final int MOUNT_EXTERNAL_WRITE = IVold.REMOUNT_MODE_WRITE;
+ /** Read-write external storage should be mounted instead of package sandbox */
+ public static final int MOUNT_EXTERNAL_FULL = IVold.REMOUNT_MODE_FULL;
private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index b9c717f03749..b60b43a8d45d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -644,7 +644,9 @@ class ZygoteConnection {
mountExternal = Zygote.MOUNT_EXTERNAL_READ;
} else if (arg.equals("--mount-external-write")) {
mountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
- } else if (arg.equals("--query-abi-list")) {
+ } else if (arg.equals("--mount-external-full")) {
+ mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
+ } else if (arg.equals("--query-abi-list")) {
abiListQuery = true;
} else if (arg.equals("--get-pid")) {
pidQuery = true;
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 823f1cc36225..9b138ebb760a 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -323,6 +323,55 @@ static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jin
return (jboolean) !queryUserAccess(uid, netId);
}
+static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst)
+{
+ if (env->GetArrayLength(addr) != len) {
+ return false;
+ }
+ env->GetByteArrayRegion(addr, 0, len, reinterpret_cast<jbyte*>(dst));
+ return true;
+}
+
+static void android_net_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArray ethAddr,
+ jbyteArray ipv4Addr, jstring ifname, jobject javaFd)
+{
+ struct arpreq req = {};
+ struct sockaddr_in& netAddrStruct = *reinterpret_cast<sockaddr_in*>(&req.arp_pa);
+ struct sockaddr& ethAddrStruct = req.arp_ha;
+
+ ethAddrStruct.sa_family = ARPHRD_ETHER;
+ if (!checkLenAndCopy(env, ethAddr, ETH_ALEN, ethAddrStruct.sa_data)) {
+ jniThrowException(env, "java/io/IOException", "Invalid ethAddr length");
+ return;
+ }
+
+ netAddrStruct.sin_family = AF_INET;
+ if (!checkLenAndCopy(env, ipv4Addr, sizeof(in_addr), &netAddrStruct.sin_addr)) {
+ jniThrowException(env, "java/io/IOException", "Invalid ipv4Addr length");
+ return;
+ }
+
+ int ifLen = env->GetStringLength(ifname);
+ // IFNAMSIZ includes the terminating NULL character
+ if (ifLen >= IFNAMSIZ) {
+ jniThrowException(env, "java/io/IOException", "ifname too long");
+ return;
+ }
+ env->GetStringUTFRegion(ifname, 0, ifLen, req.arp_dev);
+
+ req.arp_flags = ATF_COM; // Completed entry (ha valid)
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (fd < 0) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
+ return;
+ }
+ // See also: man 7 arp
+ if (ioctl(fd, SIOCSARP, &req)) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "ioctl error: %s", strerror(errno));
+ return;
+ }
+}
+
// ----------------------------------------------------------------------------
@@ -337,6 +386,7 @@ static const JNINativeMethod gNetworkUtilMethods[] = {
{ "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
{ "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
+ { "addArpEntry", "([B[BLjava/lang/String;Ljava/io/FileDescriptor;)V", (void*) android_net_utils_addArpEntry },
{ "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
{ "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
{ "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter },
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 19691e2cfbe3..364393e1c649 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -93,6 +93,7 @@ enum MountExternalKind {
MOUNT_EXTERNAL_DEFAULT = 1,
MOUNT_EXTERNAL_READ = 2,
MOUNT_EXTERNAL_WRITE = 3,
+ MOUNT_EXTERNAL_FULL = 4,
};
static void RuntimeAbort(JNIEnv* env, int line, const char* msg) {
@@ -416,7 +417,7 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
storageSource = "/mnt/runtime/read";
} else if (mount_mode == MOUNT_EXTERNAL_WRITE) {
storageSource = "/mnt/runtime/write";
- } else if (!force_mount_namespace) {
+ } else if (mount_mode != MOUNT_EXTERNAL_FULL && !force_mount_namespace) {
// Sane default of no storage visible
return true;
}
@@ -433,19 +434,44 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
}
if (GetBoolProperty(kIsolatedStorage, false)) {
- if (package_name == nullptr) {
- return true;
- }
-
- std::string pkgSandboxDir("/mnt/user");
- if (!createPkgSandbox(uid, package_name, pkgSandboxDir, error_msg)) {
- return false;
- }
- if (TEMP_FAILURE_RETRY(mount(pkgSandboxDir.c_str(), "/storage",
- nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
- pkgSandboxDir.c_str(), strerror(errno));
- return false;
+ if (mount_mode == MOUNT_EXTERNAL_FULL) {
+ storageSource = "/mnt/runtime/write";
+ if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
+ NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
+ *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
+ storageSource.string(),
+ strerror(errno));
+ return false;
+ }
+
+ // Mount user-specific symlink helper into place
+ userid_t user_id = multiuser_get_user_id(uid);
+ const String8 userSource(String8::format("/mnt/user/%d", user_id));
+ if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
+ *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", userSource.string());
+ return false;
+ }
+ if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
+ NULL, MS_BIND, NULL)) == -1) {
+ *error_msg = CREATE_ERROR("Failed to mount %s to /storage/self: %s",
+ userSource.string(),
+ strerror(errno));
+ return false;
+ }
+ } else {
+ if (package_name == nullptr) {
+ return true;
+ }
+ std::string pkgSandboxDir("/mnt/user");
+ if (!createPkgSandbox(uid, package_name, pkgSandboxDir, error_msg)) {
+ return false;
+ }
+ if (TEMP_FAILURE_RETRY(mount(pkgSandboxDir.c_str(), "/storage",
+ nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
+ *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
+ pkgSandboxDir.c_str(), strerror(errno));
+ return false;
+ }
}
} else {
if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c6931aa97a8b..c664abf55100 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3517,4 +3517,11 @@
<!-- Whether or not battery saver should be "sticky" when manually enabled. -->
<bool name="config_batterySaverStickyBehaviourDisabled">false</bool>
+
+ <!-- Model of potentially misprovisioned devices. If none is specified in an overlay, an
+ empty string is passed in. -->
+ <string name="config_misprovisionedDeviceModel" translatable="false"></string>
+
+ <!-- Brand value for attestation of misprovisioned device. -->
+ <string name="config_misprovisionedBrandValue" translatable="false"></string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7fbad164add1..9cf6ac6a61af 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3446,4 +3446,7 @@
<java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
<java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" />
+
+ <java-symbol type="string" name="config_misprovisionedDeviceModel" />
+ <java-symbol type="string" name="config_misprovisionedBrandValue" />
</resources>
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index a8094ead2972..613de45173d6 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -24,6 +24,7 @@ import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
@@ -302,6 +303,113 @@ public class BatteryStatsImplTest {
assertArrayEquals(expected, mBatteryStatsImpl.addCpuTimes(timesA, timesB));
}
+ @Test
+ public void testMulticastWakelockAcqRel() {
+ final int testUid = 10032;
+ final int acquireTimeMs = 1000;
+ final int releaseTimeMs = 1005;
+ final int currentTimeMs = 1011;
+
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+
+ // Create a Uid Object
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUid);
+ assertNotNull(u);
+
+ // Acquire and release the lock
+ u.noteWifiMulticastEnabledLocked(acquireTimeMs);
+ u.noteWifiMulticastDisabledLocked(releaseTimeMs);
+
+ // Get the total acquisition time
+ long totalTime = u.getWifiMulticastTime(currentTimeMs*1000,
+ BatteryStats.STATS_SINCE_UNPLUGGED);
+ assertEquals("Miscalculations of Multicast wakelock acquisition time",
+ (releaseTimeMs - acquireTimeMs) * 1000, totalTime);
+ }
+
+ @Test
+ public void testMulticastWakelockAcqNoRel() {
+ final int testUid = 10032;
+ final int acquireTimeMs = 1000;
+ final int currentTimeMs = 1011;
+
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+
+ // Create a Uid Object
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUid);
+ assertNotNull(u);
+
+ // Acquire the lock
+ u.noteWifiMulticastEnabledLocked(acquireTimeMs);
+
+ // Get the total acquisition time
+ long totalTime = u.getWifiMulticastTime(currentTimeMs*1000,
+ BatteryStats.STATS_SINCE_UNPLUGGED);
+ assertEquals("Miscalculations of Multicast wakelock acquisition time",
+ (currentTimeMs - acquireTimeMs) * 1000, totalTime);
+ }
+
+ @Test
+ public void testMulticastWakelockAcqAcqRelRel() {
+ final int testUid = 10032;
+ final int acquireTimeMs_1 = 1000;
+ final int acquireTimeMs_2 = 1002;
+
+ final int releaseTimeMs_1 = 1005;
+ final int releaseTimeMs_2 = 1009;
+ final int currentTimeMs = 1011;
+
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+
+ // Create a Uid Object
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUid);
+ assertNotNull(u);
+
+ // Acquire and release the lock (twice in nested way)
+ u.noteWifiMulticastEnabledLocked(acquireTimeMs_1);
+ u.noteWifiMulticastEnabledLocked(acquireTimeMs_2);
+
+ u.noteWifiMulticastDisabledLocked(releaseTimeMs_1);
+ u.noteWifiMulticastDisabledLocked(releaseTimeMs_2);
+
+ // Get the total acquisition time
+ long totalTime = u.getWifiMulticastTime(currentTimeMs*1000,
+ BatteryStats.STATS_SINCE_UNPLUGGED);
+ assertEquals("Miscalculations of Multicast wakelock acquisition time",
+ (releaseTimeMs_2 - acquireTimeMs_1) * 1000, totalTime);
+ }
+
+ @Test
+ public void testMulticastWakelockAcqRelAcqRel() {
+ final int testUid = 10032;
+ final int acquireTimeMs_1 = 1000;
+ final int acquireTimeMs_2 = 1005;
+
+ final int releaseTimeMs_1 = 1002;
+ final int releaseTimeMs_2 = 1009;
+ final int currentTimeMs = 1011;
+
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+
+ // Create a Uid Object
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUid);
+ assertNotNull(u);
+
+ // Acquire and release the lock (twice)
+ u.noteWifiMulticastEnabledLocked(acquireTimeMs_1);
+ u.noteWifiMulticastDisabledLocked(releaseTimeMs_1);
+
+ u.noteWifiMulticastEnabledLocked(acquireTimeMs_2);
+ u.noteWifiMulticastDisabledLocked(releaseTimeMs_2);
+
+ // Get the total acquisition time
+ long totalTime = u.getWifiMulticastTime(currentTimeMs*1000,
+ BatteryStats.STATS_SINCE_UNPLUGGED);
+ assertEquals("Miscalculations of Multicast wakelock acquisition time",
+ ((releaseTimeMs_1 - acquireTimeMs_1) + (releaseTimeMs_2 - acquireTimeMs_2))
+ * 1000, totalTime);
+ }
+
private void addIsolatedUid(int parentUid, int childUid) {
final BatteryStatsImpl.Uid u = mBatteryStatsImpl.getUidStatsLocked(parentUid);
u.addIsolatedUid(childUid);
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index 1be8309bcf5a..3d2a2718a9c8 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -22,9 +22,9 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.Context;
+import android.content.res.Resources;
import android.os.Build;
import android.security.KeyStore;
-import android.security.KeyStoreException;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keymaster.KeymasterDefs;
@@ -117,6 +117,37 @@ public abstract class AttestationUtils {
@NonNull public static KeymasterArguments prepareAttestationArguments(Context context,
@NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws
DeviceIdAttestationException {
+ return prepareAttestationArguments(context, idTypes,attestationChallenge, Build.BRAND);
+ }
+
+ /**
+ * Prepares Keymaster Arguments with attestation data for misprovisioned Pixel 2 device.
+ * See http://go/keyAttestationFailure and http://b/69471841 for more info.
+ * @hide should only be used by KeyChain.
+ */
+ @NonNull public static KeymasterArguments prepareAttestationArgumentsIfMisprovisioned(
+ Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws
+ DeviceIdAttestationException {
+ if (!isPotentiallyMisprovisionedDevice(context)) {
+ return null;
+ }
+ Resources resources = context.getResources();
+ String misprovisionedBrand = resources.getString(
+ com.android.internal.R.string.config_misprovisionedBrandValue);
+ return prepareAttestationArguments(
+ context, idTypes, attestationChallenge, misprovisionedBrand);
+ }
+
+ @NonNull private static boolean isPotentiallyMisprovisionedDevice(Context context) {
+ Resources resources = context.getResources();
+ String misprovisionedModel = resources.getString(
+ com.android.internal.R.string.config_misprovisionedDeviceModel);
+ return (Build.MODEL.equals(misprovisionedModel));
+ }
+
+ @NonNull private static KeymasterArguments prepareAttestationArguments(Context context,
+ @NonNull int[] idTypes, @NonNull byte[] attestationChallenge, String brand) throws
+ DeviceIdAttestationException {
// Check method arguments, retrieve requested device IDs and prepare attestation arguments.
if (attestationChallenge == null) {
throw new NullPointerException("Missing attestation challenge");
@@ -169,7 +200,7 @@ public abstract class AttestationUtils {
}
}
attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND,
- Build.BRAND.getBytes(StandardCharsets.UTF_8));
+ brand.getBytes(StandardCharsets.UTF_8));
attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE,
Build.DEVICE.getBytes(StandardCharsets.UTF_8));
attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT,
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 76fc9e350ecc..d45acdfa627e 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -405,7 +405,7 @@ import java.util.Map;
<p>
The codec in turn will return a read-only output buffer via the {@link
Callback#onOutputBufferAvailable onOutputBufferAvailable} callback in asynchronous mode, or in
- response to a {@link #dequeueOutputBuffer dequeuOutputBuffer} call in synchronous mode. After the
+ response to a {@link #dequeueOutputBuffer dequeueOutputBuffer} call in synchronous mode. After the
output buffer has been processed, call one of the {@link #releaseOutputBuffer
releaseOutputBuffer} methods to return the buffer to the codec.
<p>
diff --git a/packages/PackageInstaller/res/drawable/ic_file_download.xml b/packages/PackageInstaller/res/drawable/ic_file_download.xml
index 7ea91f5baa2d..16c6080b46d5 100644
--- a/packages/PackageInstaller/res/drawable/ic_file_download.xml
+++ b/packages/PackageInstaller/res/drawable/ic_file_download.xml
@@ -18,7 +18,8 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
- android:viewportHeight="24">
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#000000"
diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml
index c8a80ac57c00..6b45a479836e 100644
--- a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml
+++ b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:alpha="0.24" android:color="?android:attr/colorBackground" />
+ <item android:alpha="0.24" android:color="@android:color/black" />
</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml
index 8dcfdbb8cf1e..5ef085b32dd6 100644
--- a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml
+++ b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:alpha="0.47" android:color="?android:attr/colorBackground" />
+ <item android:alpha="0.47" android:color="@android:color/black" />
</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml
index 34de5489a28b..81f63a48fb9a 100644
--- a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml
+++ b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:alpha="0.3" android:color="?android:attr/colorForeground" />
+ <item android:alpha="0.3" android:color="@android:color/white" />
</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml
index 15944c3a2a07..61f4e26cf00b 100644
--- a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml
+++ b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?android:attr/colorForeground" />
+ <item android:color="@android:color/white" />
</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index a91c45dd59af..a84222909072 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -57,9 +57,7 @@ public interface BluetoothCallback {
default void onDeviceAdded(CachedBluetoothDevice cachedDevice) {}
/**
- * It will be called when a remote device that was
- * found in the last discovery and is not found in the current discovery.
- * It is listening {@link android.bluetooth.BluetoothDevice#ACTION_DISAPPEARED}
+ * It will be called when requiring to remove a remote device from CachedBluetoothDevice list
*
* @param cachedDevice the Bluetooth device.
*/
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 049761836541..25e683100ee8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -81,7 +81,6 @@ public class BluetoothEventManager {
addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));
addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));
addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
- addHandler(BluetoothDevice.ACTION_DISAPPEARED, new DeviceDisappearedHandler());
addHandler(BluetoothDevice.ACTION_NAME_CHANGED, new NameChangedHandler());
addHandler(BluetoothDevice.ACTION_ALIAS_CHANGED, new NameChangedHandler());
@@ -106,6 +105,7 @@ public class BluetoothEventManager {
new AudioModeChangedHandler());
mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
+ mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
}
public void setReceiverHandler(android.os.Handler handler) {
@@ -298,24 +298,6 @@ public class BluetoothEventManager {
}
}
- private class DeviceDisappearedHandler implements Handler {
- public void onReceive(Context context, Intent intent,
- BluetoothDevice device) {
- CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
- if (cachedDevice == null) {
- Log.w(TAG, "received ACTION_DISAPPEARED for an unknown device: " + device);
- return;
- }
- if (CachedBluetoothDeviceManager.onDeviceDisappeared(cachedDevice)) {
- synchronized (mCallbacks) {
- for (BluetoothCallback callback : mCallbacks) {
- callback.onDeviceDeleted(cachedDevice);
- }
- }
- }
- }
- }
-
private class NameChangedHandler implements Handler {
public void onReceive(Context context, Intent intent,
BluetoothDevice device) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 33ee56991932..68e1dfcfe959 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -72,14 +72,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
- // Following constants indicate the user's choices of Phone book/message access settings
- // User hasn't made any choice or settings app has wiped out the memory
- public final static int ACCESS_UNKNOWN = 0;
- // User has accepted the connection and let Settings app remember the decision
- public final static int ACCESS_ALLOWED = 1;
- // User has rejected the connection and let Settings app remember the decision
- public final static int ACCESS_REJECTED = 2;
-
// How many times user should reject the connection to make the choice persist.
private final static int MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST = 2;
@@ -659,9 +651,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
void onBondingStateChanged(int bondState) {
if (bondState == BluetoothDevice.BOND_NONE) {
mProfiles.clear();
- setPhonebookPermissionChoice(ACCESS_UNKNOWN);
- setMessagePermissionChoice(ACCESS_UNKNOWN);
- setSimPermissionChoice(ACCESS_UNKNOWN);
+ mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
+ mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
+ mDevice.setSimAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
mMessageRejectionCount = 0;
saveMessageRejectionCount();
}
@@ -767,26 +759,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
void onDeviceAttributesChanged();
}
- public int getPhonebookPermissionChoice() {
- int permission = mDevice.getPhonebookAccessPermission();
- if (permission == BluetoothDevice.ACCESS_ALLOWED) {
- return ACCESS_ALLOWED;
- } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
- return ACCESS_REJECTED;
- }
- return ACCESS_UNKNOWN;
- }
-
- public void setPhonebookPermissionChoice(int permissionChoice) {
- int permission = BluetoothDevice.ACCESS_UNKNOWN;
- if (permissionChoice == ACCESS_ALLOWED) {
- permission = BluetoothDevice.ACCESS_ALLOWED;
- } else if (permissionChoice == ACCESS_REJECTED) {
- permission = BluetoothDevice.ACCESS_REJECTED;
- }
- mDevice.setPhonebookAccessPermission(permission);
- }
-
// Migrates data from old data store (in Settings app's shared preferences) to new (in Bluetooth
// app's shared preferences).
private void migratePhonebookPermissionChoice() {
@@ -797,10 +769,11 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
}
if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
- int oldPermission = preferences.getInt(mDevice.getAddress(), ACCESS_UNKNOWN);
- if (oldPermission == ACCESS_ALLOWED) {
+ int oldPermission =
+ preferences.getInt(mDevice.getAddress(), BluetoothDevice.ACCESS_UNKNOWN);
+ if (oldPermission == BluetoothDevice.ACCESS_ALLOWED) {
mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
- } else if (oldPermission == ACCESS_REJECTED) {
+ } else if (oldPermission == BluetoothDevice.ACCESS_REJECTED) {
mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
}
}
@@ -810,46 +783,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
editor.commit();
}
- public int getMessagePermissionChoice() {
- int permission = mDevice.getMessageAccessPermission();
- if (permission == BluetoothDevice.ACCESS_ALLOWED) {
- return ACCESS_ALLOWED;
- } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
- return ACCESS_REJECTED;
- }
- return ACCESS_UNKNOWN;
- }
-
- public void setMessagePermissionChoice(int permissionChoice) {
- int permission = BluetoothDevice.ACCESS_UNKNOWN;
- if (permissionChoice == ACCESS_ALLOWED) {
- permission = BluetoothDevice.ACCESS_ALLOWED;
- } else if (permissionChoice == ACCESS_REJECTED) {
- permission = BluetoothDevice.ACCESS_REJECTED;
- }
- mDevice.setMessageAccessPermission(permission);
- }
-
- public int getSimPermissionChoice() {
- int permission = mDevice.getSimAccessPermission();
- if (permission == BluetoothDevice.ACCESS_ALLOWED) {
- return ACCESS_ALLOWED;
- } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
- return ACCESS_REJECTED;
- }
- return ACCESS_UNKNOWN;
- }
-
- void setSimPermissionChoice(int permissionChoice) {
- int permission = BluetoothDevice.ACCESS_UNKNOWN;
- if (permissionChoice == ACCESS_ALLOWED) {
- permission = BluetoothDevice.ACCESS_ALLOWED;
- } else if (permissionChoice == ACCESS_REJECTED) {
- permission = BluetoothDevice.ACCESS_REJECTED;
- }
- mDevice.setSimAccessPermission(permission);
- }
-
// Migrates data from old data store (in Settings app's shared preferences) to new (in Bluetooth
// app's shared preferences).
private void migrateMessagePermissionChoice() {
@@ -860,10 +793,11 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
}
if (mDevice.getMessageAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
- int oldPermission = preferences.getInt(mDevice.getAddress(), ACCESS_UNKNOWN);
- if (oldPermission == ACCESS_ALLOWED) {
+ int oldPermission =
+ preferences.getInt(mDevice.getAddress(), BluetoothDevice.ACCESS_UNKNOWN);
+ if (oldPermission == BluetoothDevice.ACCESS_ALLOWED) {
mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
- } else if (oldPermission == ACCESS_REJECTED) {
+ } else if (oldPermission == BluetoothDevice.ACCESS_REJECTED) {
mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
}
}
@@ -908,14 +842,14 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
if (BluetoothUuid.containsAnyUuid(uuids, PbapServerProfile.PBAB_CLIENT_UUIDS)) {
// The pairing dialog now warns of phone-book access for paired devices.
// No separate prompt is displayed after pairing.
- if (getPhonebookPermissionChoice() == CachedBluetoothDevice.ACCESS_UNKNOWN) {
+ if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
if (mDevice.getBluetoothClass().getDeviceClass()
== BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE ||
mDevice.getBluetoothClass().getDeviceClass()
== BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET) {
- setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
+ mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
} else {
- setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_REJECTED);
+ mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
}
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index e05f9fdc1eeb..f223176795b8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -241,7 +241,6 @@ public class LocalBluetoothProfileManagerTest {
mShadowBluetoothAdapter.setSupportedProfiles(null);
mProfileManager = new LocalBluetoothProfileManager(mContext, mLocalBluetoothAdapter,
mDeviceManager, mEventManager);
- mEventManager.registerProfileIntentReceiver();
// Refer to BluetoothControllerImpl, it will call setReceiverHandler after
// LocalBluetoothProfileManager created.
mEventManager.setReceiverHandler(null);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c9ba26804e89..091350334835 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -32,6 +32,7 @@ java_library {
android_library {
name: "SystemUI-core",
srcs: [
+ "src/**/*.kt",
"src/**/*.java",
"src/**/I*.aidl",
],
@@ -73,6 +74,59 @@ android_library {
],
}
+android_library {
+ name: "SystemUI-tests",
+ manifest: "tests/AndroidManifest.xml",
+ resource_dirs: [
+ "tests/res",
+ "res-keyguard",
+ "res",
+ ],
+ srcs: [
+ "tests/src/**/*.kt",
+ "tests/src/**/*.java",
+ "src/**/*.kt",
+ "src/**/*.java",
+ "src/**/I*.aidl",
+ ],
+ static_libs: [
+ "SystemUIPluginLib",
+ "SystemUISharedLib",
+ "SettingsLib",
+ "androidx.car_car",
+ "androidx.legacy_legacy-support-v4",
+ "androidx.recyclerview_recyclerview",
+ "androidx.preference_preference",
+ "androidx.appcompat_appcompat",
+ "androidx.mediarouter_mediarouter",
+ "androidx.palette_palette",
+ "androidx.legacy_legacy-preference-v14",
+ "androidx.leanback_leanback",
+ "androidx.slice_slice-core",
+ "androidx.slice_slice-view",
+ "androidx.slice_slice-builders",
+ "androidx.arch.core_core-runtime",
+ "androidx.lifecycle_lifecycle-extensions",
+ "SystemUI-tags",
+ "SystemUI-proto",
+ "metrics-helper-lib",
+ "android-support-test",
+ "mockito-target-inline-minus-junit4",
+ "testables",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.runner",
+ "telephony-common",
+ "android.car",
+ "android.test.base",
+ ],
+ aaptflags: [
+ "--extra-packages",
+ "com.android.keyguard:com.android.systemui",
+ ],
+}
+
android_app {
name: "SystemUI",
static_libs: [
diff --git a/packages/SystemUI/docs/kotlin-in-sysui.md b/packages/SystemUI/docs/kotlin-in-sysui.md
new file mode 100644
index 000000000000..1bf24f63498b
--- /dev/null
+++ b/packages/SystemUI/docs/kotlin-in-sysui.md
@@ -0,0 +1,22 @@
+# Kotlin in SystemUI
+
+Queue "it's happening" gif.
+
+Kotlin is probably going to be a bit of a wild west for a while, but please
+try to follow these guidelines as much as possible.
+
+ - No semi-colons: they are optional, we probably don't want them in the
+ future, so let's just not add them.
+ - No DSLs: sysui is complicated enough as is, let's not add more layers at
+ the moment.
+ - Only use extension functions for keeping complex code locality: Don't use
+ extension functions to add methods to android classes that you always wished
+ were there, instead add them directly to the class and save us the extension.
+ - inline, reified, and de-compisition can all be great things: just make sure
+ you know what they do and why you are using them.
+
+# Recommended reading
+
+ - [Kotlin](https://kotlinlang.org/)
+ - [AndroidX-KTX](https://www.youtube.com/watch?v=st1XVfkDWqk)
+ - [Performance and Kotlin tricks](https://www.youtube.com/watch?v=6P20npkvcb8)
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 412e89ad169a..7e35f5f70575 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -952,6 +952,7 @@
<dimen name="nav_content_padding">0dp</dimen>
<dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>
<dimen name="nav_quick_scrub_track_thickness">10dp</dimen>
+ <dimen name="nav_home_back_gesture_drag_limit">60dp</dimen>
<!-- Navigation bar shadow params. -->
<dimen name="nav_key_button_shadow_offset_x">0dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2deec5e30e32..36f97cd64642 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1038,6 +1038,15 @@
<!-- Message for add user confirmation dialog - short version. [CHAR LIMIT=none] -->
<string name="user_add_user_message_short" msgid="1511354412249044381">When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users. </string>
+ <!-- Title for the dialog that lets users know that the maximum allowed number of users on the device has been reached. [CHAR LIMIT=35]-->
+ <string name="user_limit_reached_title">User limit reached</string>
+
+ <!-- Message that tells people what's the maximum number of uses allowed on the device. [CHAR_LIMIT=NONE]-->
+ <plurals name="user_limit_reached_message">
+ <item quantity="one">Only one user can be created.</item>
+ <item quantity="other">You can add up to <xliff:g id="count" example="3">%d</xliff:g> users.</item>
+ </plurals>
+
<!-- Title of the confirmation dialog for deleting a user [CHAR LIMIT=NONE] -->
<string name="user_remove_user_title">Remove user?</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 47ec5cd0175d..8442dd13a85b 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -321,12 +321,15 @@
<item name="*android:isLightTheme">false</item>
</style>
- <style name="Theme.SystemUI.Light" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
+ <style name="Theme.SystemUI.Light">
<item name="wallpaperTextColor">@*android:color/primary_text_material_light</item>
<item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_light</item>
<item name="android:colorError">@*android:color/error_color_material_light</item>
<item name="android:colorControlHighlight">#40000000</item>
<item name="passwordStyle">@style/PasswordTheme.Light</item>
+
+ <!-- Needed for MediaRoute chooser dialog -->
+ <item name="*android:isLightTheme">true</item>
</style>
<style name="LockPatternStyle">
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 9bf781678962..5ffdc7b221f3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -145,6 +145,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
mLockPatternView = findViewById(R.id.lockPatternView);
mLockPatternView.setSaveEnabled(false);
mLockPatternView.setOnPatternListener(new UnlockPatternListener());
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
+ KeyguardUpdateMonitor.getCurrentUser()));
// vibrate mode will be the same for the life of this screen
mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index d5383b975211..f6fec5456ed8 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -82,7 +82,6 @@ public class BatteryMeterView extends LinearLayout implements
private int mLightModeBackgroundColor;
private int mLightModeFillColor;
- private float mDarkIntensity;
private int mUser;
/**
@@ -321,8 +320,6 @@ public class BatteryMeterView extends LinearLayout implements
@Override
public void onDarkChanged(Rect area, float darkIntensity, int tint) {
- mDarkIntensity = darkIntensity;
-
float intensity = DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0;
mNonAdaptedForegroundColor = getColorForDarkIntensity(
intensity, mLightModeFillColor, mDarkModeFillColor);
@@ -342,14 +339,6 @@ public class BatteryMeterView extends LinearLayout implements
}
}
- public void setFillColor(int color) {
- if (mLightModeFillColor == color) {
- return;
- }
- mLightModeFillColor = color;
- onDarkChanged(new Rect(), mDarkIntensity, DarkIconDispatcher.DEFAULT_ICON_TINT);
- }
-
private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) {
return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 39e315566daf..acc7b49d9c35 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -274,7 +274,9 @@ public class ImageWallpaper extends WallpaperService {
// Sometimes a wallpaper is not large enough to cover the screen in one dimension.
// Call updateSurfaceSize -- it will only actually do the update if the dimensions
// should change
- if (newRotation != mLastRotation) {
+ if (newRotation != mLastRotation
+ || mDisplayWidthAtLastSurfaceSizeUpdate != displayInfo.logicalWidth
+ || mDisplayHeightAtLastSurfaceSizeUpdate != displayInfo.logicalHeight) {
// Update surface size (if necessary)
if (!updateSurfaceSize(getSurfaceHolder(), displayInfo, true /* forDraw */)) {
return; // had to reload wallpaper, will retry later
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index ad853f18993e..cafdc10beefa 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -89,6 +89,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private @InteractionType int mInteractionFlags;
private boolean mIsEnabled;
private int mCurrentBoundedUserId = -1;
+ private float mBackButtonAlpha;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@@ -180,6 +181,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
long token = Binder.clearCallingIdentity();
try {
+ mBackButtonAlpha = alpha;
mHandler.post(() -> {
notifyBackButtonAlphaChanged(alpha, animate);
});
@@ -309,6 +311,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
}
+ public float getBackButtonAlpha() {
+ return mBackButtonAlpha;
+ }
+
public void startConnectionToCurrentUser() {
if (mHandler.getLooper() != Looper.myLooper()) {
mHandler.post(mConnectionRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 6de054313cb7..929713c2c50b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.content.Context;
@@ -113,7 +114,7 @@ public class NotificationRemoteInputManager implements Dumpable {
} catch (RemoteException e) {
}
return mCallback.handleRemoteViewClick(view, pendingIntent, fillInIntent,
- () -> superOnClickHandler(view, pendingIntent, fillInIntent));
+ () -> super.onClickHandler(view, pendingIntent, fillInIntent));
}
private void logActionClick(View view) {
@@ -151,10 +152,11 @@ public class NotificationRemoteInputManager implements Dumpable {
return null;
}
- private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
- Intent fillInIntent) {
- return super.onClickHandler(view, pendingIntent, fillInIntent,
- WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+ @Override
+ protected ActivityOptions getActivityOptions(Context context) {
+ ActivityOptions options = super.getActivityOptions(context);
+ options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+ return options;
}
private boolean handleRemoteInput(View view, PendingIntent pendingIntent) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index b376c007bbe4..da6401c2d62a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -164,7 +164,6 @@ public class UserGridRecyclerView extends PagedListView implements
private final Resources mRes;
private final String mGuestName;
private final String mNewUserName;
- private AlertDialog mDialog;
// View that holds the add user button. Used to enable/disable the view
private View mAddUserView;
// User record for the add user. Need to call notifyUserSelected only if the user
@@ -221,23 +220,9 @@ public class UserGridRecyclerView extends PagedListView implements
// Disable button so it cannot be clicked multiple times
mAddUserView = holder.mView;
mAddUserView.setEnabled(false);
-
- String message = mRes.getString(R.string.user_add_user_message_setup)
- .concat(System.getProperty("line.separator"))
- .concat(System.getProperty("line.separator"))
- .concat(mRes.getString(R.string.user_add_user_message_update));
-
mAddUserRecord = userRecord;
- mDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
- .setTitle(R.string.user_add_user_title)
- .setMessage(message)
- .setNegativeButton(android.R.string.cancel, this)
- .setPositiveButton(android.R.string.ok, this)
- .setOnCancelListener(this)
- .create();
- // Sets window flags for the SysUI dialog
- SystemUIDialog.applyFlags(mDialog);
- mDialog.show();
+
+ handleAddUserClicked();
return;
}
// If the user doesn't want to be a guest or add a user, switch to the user selected
@@ -247,6 +232,47 @@ public class UserGridRecyclerView extends PagedListView implements
}
+ private void handleAddUserClicked() {
+ if (mCarUserManagerHelper.isUserLimitReached()) {
+ mAddUserView.setEnabled(true);
+ showMaxUserLimitReachedDialog();
+ } else {
+ showConfirmAddUserDialog();
+ }
+ }
+
+ private void showMaxUserLimitReachedDialog() {
+ AlertDialog maxUsersDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
+ .setTitle(R.string.user_limit_reached_title)
+ .setMessage(getResources().getQuantityString(
+ R.plurals.user_limit_reached_message,
+ mCarUserManagerHelper.getMaxSupportedRealUsers(),
+ mCarUserManagerHelper.getMaxSupportedRealUsers()))
+ .setPositiveButton(android.R.string.ok, null)
+ .create();
+ // Sets window flags for the SysUI dialog
+ SystemUIDialog.applyFlags(maxUsersDialog);
+ maxUsersDialog.show();
+ }
+
+ private void showConfirmAddUserDialog() {
+ String message = mRes.getString(R.string.user_add_user_message_setup)
+ .concat(System.getProperty("line.separator"))
+ .concat(System.getProperty("line.separator"))
+ .concat(mRes.getString(R.string.user_add_user_message_update));
+
+ AlertDialog addUserDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
+ .setTitle(R.string.user_add_user_title)
+ .setMessage(message)
+ .setNegativeButton(android.R.string.cancel, this)
+ .setPositiveButton(android.R.string.ok, this)
+ .setOnCancelListener(this)
+ .create();
+ // Sets window flags for the SysUI dialog
+ SystemUIDialog.applyFlags(addUserDialog);
+ addUserDialog.show();
+ }
+
private void notifyUserSelected(UserRecord userRecord) {
// Notify the listener which user was selected
if (mUserSelectionListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 4c1938a62137..46019e3b48ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -417,7 +417,6 @@ public abstract class ExpandableView extends FrameLayout {
public void setWillBeGone(boolean willBeGone) {
mWillBeGone = willBeGone;
- invalidate();
}
public int getMinClipTopAmount() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index f48c3f5f5594..587b40d926d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -163,12 +163,16 @@ public class ButtonDispatcher {
}
public void setAlpha(float alpha, boolean animate) {
+ setAlpha(alpha, animate, (getAlpha() < alpha) ? FADE_DURATION_IN : FADE_DURATION_OUT);
+ }
+
+ public void setAlpha(float alpha, boolean animate, long duration) {
if (animate) {
if (mFadeAnimator != null) {
mFadeAnimator.cancel();
}
mFadeAnimator = ValueAnimator.ofFloat(getAlpha(), alpha);
- mFadeAnimator.setDuration(getAlpha() < alpha? FADE_DURATION_IN : FADE_DURATION_OUT);
+ mFadeAnimator.setDuration(duration);
mFadeAnimator.setInterpolator(getAlpha() < alpha ? ALPHA_IN : ALPHA_OUT);
mFadeAnimator.addListener(mFadeListener);
mFadeAnimator.addUpdateListener(mAlphaListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.java
deleted file mode 100644
index 9d40f173bf26..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.content.om.IOverlayManager;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.os.LocaleList;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-
-import com.android.systemui.ConfigurationChangedReceiver;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-public class ConfigurationControllerImpl implements ConfigurationController,
- ConfigurationChangedReceiver {
-
- private final ArrayList<ConfigurationListener> mListeners = new ArrayList<>();
- private final Configuration mLastConfig = new Configuration();
- private int mDensity;
- private float mFontScale;
- private boolean mInCarMode;
- private int mUiMode;
- private LocaleList mLocaleList;
-
- public ConfigurationControllerImpl(Context context) {
- Configuration currentConfig = context.getResources().getConfiguration();
- mFontScale = currentConfig.fontScale;
- mDensity = currentConfig.densityDpi;
- mInCarMode = (currentConfig.uiMode & Configuration.UI_MODE_TYPE_MASK)
- == Configuration.UI_MODE_TYPE_CAR;
- mUiMode = currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
- mLocaleList = currentConfig.getLocales();
- }
-
- @Override
- public void notifyThemeChanged() {
- ArrayList<ConfigurationListener> listeners = new ArrayList<>(mListeners);
-
- listeners.forEach(l -> {
- if (mListeners.contains(l)) {
- l.onThemeChanged();
- }
- });
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- // Avoid concurrent modification exception
- ArrayList<ConfigurationListener> listeners = new ArrayList<>(mListeners);
-
- listeners.forEach(l -> {
- if (mListeners.contains(l)) {
- l.onConfigChanged(newConfig);
- }
- });
- final float fontScale = newConfig.fontScale;
- final int density = newConfig.densityDpi;
- int uiMode = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
- boolean uiModeChanged = uiMode != mUiMode;
- if (density != mDensity || fontScale != mFontScale
- || (mInCarMode && uiModeChanged)) {
- listeners.forEach(l -> {
- if (mListeners.contains(l)) {
- l.onDensityOrFontScaleChanged();
- }
- });
- mDensity = density;
- mFontScale = fontScale;
- }
-
- final LocaleList localeList = newConfig.getLocales();
- if (!localeList.equals(mLocaleList)) {
- mLocaleList = localeList;
- listeners.forEach(l -> {
- if (mListeners.contains(l)) {
- l.onLocaleListChanged();
- }
- });
- }
-
- if (uiModeChanged) {
- mUiMode = uiMode;
- listeners.forEach(l -> {
- if (mListeners.contains(l)) {
- l.onUiModeChanged();
- }
- });
- }
-
- if ((mLastConfig.updateFrom(newConfig) & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
- listeners.forEach(l -> {
- if (mListeners.contains(l)) {
- l.onOverlayChanged();
- }
- });
- }
- }
-
- @Override
- public void addCallback(ConfigurationListener listener) {
- mListeners.add(listener);
- listener.onDensityOrFontScaleChanged();
- }
-
- @Override
- public void removeCallback(ConfigurationListener listener) {
- mListeners.remove(listener);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
new file mode 100644
index 000000000000..81b596c0345e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.systemui.statusbar.phone
+
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
+import android.os.LocaleList
+
+import com.android.systemui.ConfigurationChangedReceiver
+import com.android.systemui.statusbar.policy.ConfigurationController
+
+import java.util.ArrayList
+
+class ConfigurationControllerImpl(context: Context)
+ : ConfigurationController, ConfigurationChangedReceiver {
+
+ private val listeners: MutableList<ConfigurationController.ConfigurationListener> = ArrayList()
+ private val lastConfig = Configuration()
+ private var density: Int = 0
+ private var fontScale: Float = 0.toFloat()
+ private val inCarMode: Boolean
+ private var uiMode: Int = 0
+ private var localeList: LocaleList? = null
+
+ init {
+ val currentConfig = context.resources.configuration
+ fontScale = currentConfig.fontScale
+ density = currentConfig.densityDpi
+ inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
+ Configuration.UI_MODE_TYPE_CAR
+ uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
+ localeList = currentConfig.locales
+ }
+
+ override fun notifyThemeChanged() {
+ val listeners = ArrayList(listeners)
+
+ listeners.filterForEach({ this.listeners.contains(it) }) {
+ it.onThemeChanged()
+ }
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ // Avoid concurrent modification exception
+ val listeners = ArrayList(listeners)
+
+ listeners.filterForEach({ this.listeners.contains(it) }) {
+ it.onConfigChanged(newConfig)
+ }
+ val fontScale = newConfig.fontScale
+ val density = newConfig.densityDpi
+ val uiMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
+ val uiModeChanged = uiMode != this.uiMode
+ if (density != this.density || fontScale != this.fontScale ||
+ inCarMode && uiModeChanged) {
+ listeners.filterForEach({ this.listeners.contains(it) }) {
+ it.onDensityOrFontScaleChanged()
+ }
+ this.density = density
+ this.fontScale = fontScale
+ }
+
+ val localeList = newConfig.locales
+ if (localeList != this.localeList) {
+ this.localeList = localeList
+ listeners.filterForEach({ this.listeners.contains(it) }) {
+ it.onLocaleListChanged()
+ }
+ }
+
+ if (uiModeChanged) {
+ this.uiMode = uiMode
+ listeners.filterForEach({ this.listeners.contains(it) }) {
+ it.onUiModeChanged()
+ }
+ }
+
+ if (lastConfig.updateFrom(newConfig) and ActivityInfo.CONFIG_ASSETS_PATHS != 0) {
+ listeners.filterForEach({ this.listeners.contains(it) }) {
+ it.onOverlayChanged()
+ }
+ }
+ }
+
+ override fun addCallback(listener: ConfigurationController.ConfigurationListener) {
+ listeners.add(listener)
+ listener.onDensityOrFontScaleChanged()
+ }
+
+ override fun removeCallback(listener: ConfigurationController.ConfigurationListener) {
+ listeners.remove(listener)
+ }
+}
+
+// This could be done with a Collection.filter and Collection.forEach, but Collection.filter
+// creates a new array to store them in and we really don't need that here, so this provides
+// a little more optimized inline version.
+inline fun <T> Collection<T>.filterForEach(f: (T) -> Boolean, execute: (T) -> Unit) {
+ forEach {
+ if (f.invoke(it)) {
+ execute.invoke(it)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 20ea27a3f9c9..5630da6662e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -447,8 +447,8 @@ public class KeyguardStatusBarView extends RelativeLayout
R.color.light_mode_icon_color_single_tone);
float intensity = textColor == Color.WHITE ? 0 : 1;
mCarrierLabel.setTextColor(iconColor);
- mBatteryView.setFillColor(iconColor);
mIconManager.setTint(iconColor);
+ mBatteryView.setColorsFromContext(mContext);
Rect tintArea = new Rect(0, 0, 0, 0);
applyDarkness(R.id.battery, tintArea, intensity, iconColor);
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 40c8fde3f845..b84ee037b9fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -195,8 +195,14 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
@Override
public void onBackButtonAlphaChanged(float alpha, boolean animate) {
final ButtonDispatcher backButton = mNavigationBarView.getBackButton();
- backButton.setVisibility(alpha > 0 ? View.VISIBLE : View.INVISIBLE);
- backButton.setAlpha(alpha, animate);
+ if (QuickStepController.shouldhideBackButton()) {
+ // If property was changed to hide/show back button, going home will trigger
+ // launcher to to change the back button alpha to reflect property change
+ backButton.setVisibility(View.GONE);
+ } else {
+ backButton.setVisibility(alpha > 0 ? View.VISIBLE : View.INVISIBLE);
+ backButton.setAlpha(alpha, animate);
+ }
}
};
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 0d9c623b0e8f..1892d37c62d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -663,7 +663,8 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
// Always disable recents when alternate car mode UI is active.
boolean disableRecent = mUseCarModeUi || !isOverviewEnabled();
- boolean disableBack = ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0) && !useAltBack;
+ boolean disableBack = QuickStepController.shouldhideBackButton()
+ || (((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0) && !useAltBack);
// When screen pinning, don't hide back and home when connected service or back and
// recents buttons when disconnected from launcher service in screen pinning mode,
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 5d65b5645330..33ddfde845a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2967,7 +2967,6 @@ public class NotificationPanelView extends PanelView implements
}
public void updateNotificationViews() {
- if (mNotificationStackScroller == null) return;
mNotificationStackScroller.updateSpeedBumpIndex();
mNotificationStackScroller.updateFooter();
updateShowEmptyShadeView();
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 a13bebdcdc0a..bd4d790a00b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -23,6 +23,7 @@ import static com.android.systemui.Interpolators.ALPHA_OUT;
import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
import static com.android.systemui.OverviewProxyService.TAG_OPS;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -37,15 +38,24 @@ import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.Shader;
+import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
import android.util.FloatProperty;
import android.util.Log;
import android.util.Slog;
+import android.view.HapticFeedbackConstants;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewPropertyAnimator;
import android.view.WindowManagerGlobal;
import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
@@ -64,10 +74,25 @@ public class QuickStepController implements GestureHelper {
private static final float TRACK_SCALE = 0.95f;
private static final float GRADIENT_WIDTH = .75f;
+ /** Experiment to swipe home button left to execute a back key press */
+ private static final String PULL_HOME_GO_BACK_PROP = "persist.quickstepcontroller.homegoesback";
+ private static final String HIDE_BACK_BUTTON_PROP = "persist.quickstepcontroller.hideback";
+ private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60;
+ private static final long BACK_BUTTON_FADE_IN_ALPHA = 150;
+ private static final long BACK_GESTURE_POLL_TIMEOUT = 1000;
+
+ /** When the home-swipe-back gesture is disallowed, make it harder to pull */
+ private static final float DISALLOW_GESTURE_DAMPING_FACTOR = 0.16f;
+
+ /** When dragging the home button too far during back gesture, make it harder to pull */
+ private static final float EXCEED_DRAG_HOME_DAMPING_FACTOR = 0.33f;
+
private NavigationBarView mNavigationBarView;
private boolean mQuickScrubActive;
private boolean mAllowGestureDetection;
+ private boolean mBackGestureActive;
+ private boolean mCanPerformBack;
private boolean mQuickStepStarted;
private boolean mNotificationsVisibleOnDown;
private int mTouchDownX;
@@ -81,6 +106,7 @@ public class QuickStepController implements GestureHelper {
private RadialGradient mHighlight;
private float mHighlightCenter;
private AnimatorSet mTrackAnimator;
+ private ViewPropertyAnimator mHomeAnimator;
private ButtonDispatcher mHitTarget;
private View mCurrentNavigationBarView;
private boolean mIsInScreenPinning;
@@ -90,11 +116,20 @@ public class QuickStepController implements GestureHelper {
private final OverviewProxyService mOverviewEventSender;
private final int mTrackThickness;
private final int mTrackEndPadding;
+ private final int mHomeBackGestureDragLimit;
private final Context mContext;
private final Matrix mTransformGlobalMatrix = new Matrix();
private final Matrix mTransformLocalMatrix = new Matrix();
private final Paint mTrackPaint = new Paint();
+ public static boolean swipeHomeGoBackGestureEnabled() {
+ return SystemProperties.getBoolean(PULL_HOME_GO_BACK_PROP, false);
+ }
+ public static boolean shouldhideBackButton() {
+ return swipeHomeGoBackGestureEnabled()
+ && SystemProperties.getBoolean(HIDE_BACK_BUTTON_PROP, false);
+ }
+
private final FloatProperty<QuickStepController> mTrackAlphaProperty =
new FloatProperty<QuickStepController>("TrackAlpha") {
@Override
@@ -148,18 +183,34 @@ public class QuickStepController implements GestureHelper {
}
};
+ private final Runnable mExecuteBackRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (canPerformHomeBackGesture()) {
+ performBack();
+ mHandler.postDelayed(this, BACK_GESTURE_POLL_TIMEOUT);
+ }
+ }
+ };
+
public QuickStepController(Context context) {
final Resources res = context.getResources();
mContext = context;
mOverviewEventSender = Dependency.get(OverviewProxyService.class);
mTrackThickness = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_thickness);
mTrackEndPadding = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
+ mHomeBackGestureDragLimit =
+ res.getDimensionPixelSize(R.dimen.nav_home_back_gesture_drag_limit);
mTrackPaint.setAntiAlias(true);
mTrackPaint.setDither(true);
}
public void setComponents(NavigationBarView navigationBarView) {
mNavigationBarView = navigationBarView;
+
+ mNavigationBarView.getBackButton().setVisibility(shouldhideBackButton()
+ ? View.GONE
+ : View.VISIBLE);
}
/**
@@ -218,8 +269,10 @@ public class QuickStepController implements GestureHelper {
mNavigationBarView.transformMatrixToGlobal(mTransformGlobalMatrix);
mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
mQuickStepStarted = false;
+ mBackGestureActive = false;
mAllowGestureDetection = true;
mNotificationsVisibleOnDown = !mNavigationBarView.isNotificationsFullyCollapsed();
+ mCanPerformBack = canPerformHomeBackGesture();
break;
}
case MotionEvent.ACTION_MOVE: {
@@ -255,7 +308,7 @@ public class QuickStepController implements GestureHelper {
}
// Decide to start quickstep if dragging away from the navigation bar, otherwise in
// the parallel direction, decide to start quickscrub. Only one may run.
- if (!mQuickScrubActive && exceededSwipeUpTouchSlop) {
+ if (!mBackGestureActive && !mQuickScrubActive && exceededSwipeUpTouchSlop) {
if (mNavigationBarView.isQuickStepSwipeUpEnabled()
&& !mNotificationsVisibleOnDown) {
startQuickStep(event);
@@ -275,10 +328,14 @@ public class QuickStepController implements GestureHelper {
final boolean allowDrag = !mDragPositive
? offset < 0 && pos < touchDown : offset >= 0 && pos > touchDown;
float scrubFraction = Utilities.clamp(Math.abs(offset) * 1f / trackSize, 0, 1);
- if (allowDrag) {
+ if (!mQuickScrubActive && !mBackGestureActive && exceededScrubTouchSlop) {
// Passing the drag slop then touch slop will start quick step
- if (!mQuickScrubActive && exceededScrubTouchSlop) {
+ if (allowDrag) {
startQuickScrub();
+ } else if (swipeHomeGoBackGestureEnabled()
+ && mNavigationBarView.getDownHitTarget() == HIT_TARGET_HOME
+ && mDragPositive ? pos < touchDown : pos > touchDown) {
+ startBackGesture();
}
}
@@ -294,23 +351,41 @@ public class QuickStepController implements GestureHelper {
}
mHighlightCenter = x;
mNavigationBarView.invalidate();
+ } else if (mBackGestureActive) {
+ int diff = pos - touchDown;
+ // If dragging the incorrect direction after starting back gesture or unable
+ // to execute back functionality, then move home but dampen its distance
+ if (!mCanPerformBack || (mDragPositive ? diff > 0 : diff < 0)) {
+ diff *= DISALLOW_GESTURE_DAMPING_FACTOR;
+ } if (Math.abs(diff) > mHomeBackGestureDragLimit) {
+ // Once the user drags the home button past a certain limit, the distance
+ // will lessen as the home button dampens showing that it was pulled too far
+ float distanceAfterDragLimit = (Math.abs(diff) - mHomeBackGestureDragLimit)
+ * EXCEED_DRAG_HOME_DAMPING_FACTOR;
+ diff = (int)(distanceAfterDragLimit + mHomeBackGestureDragLimit);
+ if (mDragPositive) {
+ diff *= -1;
+ }
+ }
+ moveHomeButton(diff);
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
endQuickScrub(true /* animate */);
+ endBackGesture();
break;
}
if (shouldProxyEvents(action)) {
proxyMotionEvents(event);
}
- return mQuickScrubActive || mQuickStepStarted || deadZoneConsumed;
+ return mBackGestureActive || mQuickScrubActive || mQuickStepStarted || deadZoneConsumed;
}
private boolean shouldProxyEvents(int action) {
- if (!mQuickScrubActive && !mIsInScreenPinning) {
+ if (!mBackGestureActive && !mQuickScrubActive && !mIsInScreenPinning) {
// Allow down, cancel and up events, move and other events are passed if notifications
// are not showing and disabled gestures (such as long press) are not executed
switch (action) {
@@ -501,6 +576,42 @@ public class QuickStepController implements GestureHelper {
}
}
+ private void startBackGesture() {
+ if (!mBackGestureActive) {
+ mBackGestureActive = true;
+ mNavigationBarView.getHomeButton().abortCurrentGesture();
+ if (mCanPerformBack) {
+ if (!shouldhideBackButton()) {
+ mNavigationBarView.getBackButton().setAlpha(0 /* alpha */, true /* animate */,
+ BACK_BUTTON_FADE_OUT_ALPHA);
+ }
+ performBack();
+ }
+ mHandler.removeCallbacks(mExecuteBackRunnable);
+ mHandler.postDelayed(mExecuteBackRunnable, BACK_GESTURE_POLL_TIMEOUT);
+ }
+ }
+
+ private void endBackGesture() {
+ if (mBackGestureActive) {
+ mHandler.removeCallbacks(mExecuteBackRunnable);
+ mHomeAnimator = mNavigationBarView.getHomeButton().getCurrentView()
+ .animate()
+ .setDuration(BACK_BUTTON_FADE_IN_ALPHA)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ if (mIsVertical) {
+ mHomeAnimator.translationY(0);
+ } else {
+ mHomeAnimator.translationX(0);
+ }
+ mHomeAnimator.start();
+ if (!shouldhideBackButton()) {
+ mNavigationBarView.getBackButton().setAlpha(
+ mOverviewEventSender.getBackButtonAlpha(), true /* animate */);
+ }
+ }
+ }
+
private void animateEnd() {
if (mTrackAnimator != null) {
mTrackAnimator.cancel();
@@ -530,6 +641,19 @@ public class QuickStepController implements GestureHelper {
updateHighlight();
}
+ private void moveHomeButton(float pos) {
+ if (mHomeAnimator != null) {
+ mHomeAnimator.cancel();
+ mHomeAnimator = null;
+ }
+ final View homeButton = mNavigationBarView.getHomeButton().getCurrentView();
+ if (mIsVertical) {
+ homeButton.setTranslationY(pos);
+ } else {
+ homeButton.setTranslationX(pos);
+ }
+ }
+
private void updateHighlight() {
if (mTrackRect.isEmpty()) {
return;
@@ -548,6 +672,25 @@ public class QuickStepController implements GestureHelper {
mTrackPaint.setShader(mHighlight);
}
+ private boolean canPerformHomeBackGesture() {
+ return swipeHomeGoBackGestureEnabled() && mOverviewEventSender.getBackButtonAlpha() > 0;
+ }
+
+ private void performBack() {
+ sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
+ sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
+ mNavigationBarView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+ }
+
+ private void sendEvent(int action, int code) {
+ long when = SystemClock.uptimeMillis();
+ final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
+ 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
+ KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
+ InputDevice.SOURCE_KEYBOARD);
+ InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ }
+
private boolean proxyMotionEvents(MotionEvent event) {
final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
event.transform(mTransformGlobalMatrix);
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 9ee55324efa2..6057614f26ee 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -21,28 +21,12 @@ LOCAL_MODULE_TAGS := tests
LOCAL_JACK_FLAGS := --multi-dex native
LOCAL_DX_FLAGS := --multi-dex
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTOC_FLAGS := -I$(LOCAL_PATH)/..
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
-
LOCAL_PACKAGE_NAME := SystemUITests
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
- $(call all-Iaidl-files-under, src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
LOCAL_STATIC_ANDROID_LIBRARIES := \
- SystemUI-core
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- metrics-helper-lib \
- android-support-test \
- mockito-target-inline-minus-junit4 \
- testables \
- truth-prebuilt \
+ SystemUI-tests
LOCAL_MULTILIB := both
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 64f96da09b08..8b1324a77a5e 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -72,6 +72,7 @@
tools:replace="android:authorities"
android:authorities="${applicationId}.lifecycle-tests"
android:exported="false"
+ android:enabled="false"
android:multiprocess="true" />
<provider android:name="com.android.systemui.keyguard.KeyguardSliceProvider"
android:authorities="com.android.systemui.test.keyguard.disabled"
@@ -83,6 +84,7 @@
android:name="androidx.core.content.FileProvider"
android:authorities="com.android.systemui.test.fileprovider"
android:exported="false"
+ android:enabled="false"
tools:replace="android:authorities"
android:grantUriPermissions="true" />
</application>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
new file mode 100644
index 000000000000..0d13e975e56a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.systemui.statusbar.phone
+
+import android.support.test.filters.SmallTest
+import android.testing.AndroidTestingRunner
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ConfigurationControllerImplTest : SysuiTestCase() {
+
+ private val mConfigurationController =
+ com.android.systemui.statusbar.phone.ConfigurationControllerImpl(mContext)
+
+ @Test
+ fun testThemeChange() {
+ val listener = mock(ConfigurationListener::class.java)
+ mConfigurationController.addCallback(listener)
+
+ mConfigurationController.notifyThemeChanged()
+ verify(listener).onThemeChanged()
+ }
+
+ @Test
+ fun testRemoveListenerDuringCallback() {
+ val listener = mock(ConfigurationListener::class.java)
+ mConfigurationController.addCallback(listener)
+ val listener2 = mock(ConfigurationListener::class.java)
+ mConfigurationController.addCallback(listener2)
+
+ doAnswer {
+ mConfigurationController.removeCallback(listener2)
+ null
+ }.`when`(listener).onThemeChanged()
+
+ mConfigurationController.notifyThemeChanged()
+ verify(listener).onThemeChanged()
+ verify(listener2, never()).onThemeChanged()
+ }
+}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index b3f6bd1d41cc..998e441f67d8 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -45,8 +45,10 @@ import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.provider.Settings;
import android.util.ArrayMap;
@@ -657,33 +659,35 @@ public class AppOpsService extends IAppOpsService.Stub {
}
});
- StorageManagerInternal storageManagerInternal = LocalServices.getService(
- StorageManagerInternal.class);
- storageManagerInternal.addExternalStoragePolicy(
- new StorageManagerInternal.ExternalStorageMountPolicy() {
- @Override
- public int getMountMode(int uid, String packageName) {
- if (Process.isIsolated(uid)) {
- return Zygote.MOUNT_EXTERNAL_NONE;
- }
- if (noteOperation(AppOpsManager.OP_READ_EXTERNAL_STORAGE, uid,
- packageName) != AppOpsManager.MODE_ALLOWED) {
- return Zygote.MOUNT_EXTERNAL_NONE;
- }
- if (noteOperation(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE, uid,
- packageName) != AppOpsManager.MODE_ALLOWED) {
- return Zygote.MOUNT_EXTERNAL_READ;
+ if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ StorageManagerInternal storageManagerInternal = LocalServices.getService(
+ StorageManagerInternal.class);
+ storageManagerInternal.addExternalStoragePolicy(
+ new StorageManagerInternal.ExternalStorageMountPolicy() {
+ @Override
+ public int getMountMode(int uid, String packageName) {
+ if (Process.isIsolated(uid)) {
+ return Zygote.MOUNT_EXTERNAL_NONE;
+ }
+ if (noteOperation(AppOpsManager.OP_READ_EXTERNAL_STORAGE, uid,
+ packageName) != AppOpsManager.MODE_ALLOWED) {
+ return Zygote.MOUNT_EXTERNAL_NONE;
+ }
+ if (noteOperation(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE, uid,
+ packageName) != AppOpsManager.MODE_ALLOWED) {
+ return Zygote.MOUNT_EXTERNAL_READ;
+ }
+ return Zygote.MOUNT_EXTERNAL_WRITE;
}
- return Zygote.MOUNT_EXTERNAL_WRITE;
- }
- @Override
- public boolean hasExternalStorage(int uid, String packageName) {
- final int mountMode = getMountMode(uid, packageName);
- return mountMode == Zygote.MOUNT_EXTERNAL_READ
- || mountMode == Zygote.MOUNT_EXTERNAL_WRITE;
- }
- });
+ @Override
+ public boolean hasExternalStorage(int uid, String packageName) {
+ final int mountMode = getMountMode(uid, packageName);
+ return mountMode == Zygote.MOUNT_EXTERNAL_READ
+ || mountMode == Zygote.MOUNT_EXTERNAL_WRITE;
+ }
+ });
+ }
}
public void packageRemoved(int uid, String packageName) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1d01a572bbfb..a8269824da0e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15466,12 +15466,6 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized(this) {
// !!! TODO: currently no check here that we're already bound
- BatteryStatsImpl.Uid.Pkg.Serv ss = null;
- BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
- synchronized (stats) {
- ss = stats.getServiceStatsLocked(app.uid, app.packageName, app.name);
- }
-
// Backup agent is now in use, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
@@ -15482,7 +15476,7 @@ public class ActivityManagerService extends IActivityManager.Stub
+ app.packageName + ": " + e);
}
- BackupRecord r = new BackupRecord(ss, app, backupMode);
+ BackupRecord r = new BackupRecord(app, backupMode);
ComponentName hostingName =
(backupMode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL)
? new ComponentName(app.packageName, app.backupAgentName)
diff --git a/services/core/java/com/android/server/am/BackupRecord.java b/services/core/java/com/android/server/am/BackupRecord.java
index 5fa7e6a37133..40ad383e7d58 100644
--- a/services/core/java/com/android/server/am/BackupRecord.java
+++ b/services/core/java/com/android/server/am/BackupRecord.java
@@ -16,8 +16,6 @@
package com.android.server.am;
-import com.android.internal.os.BatteryStatsImpl;
-
import android.content.pm.ApplicationInfo;
/** @hide */
@@ -28,7 +26,6 @@ final class BackupRecord {
public static final int RESTORE = 2;
public static final int RESTORE_FULL = 3;
- final BatteryStatsImpl.Uid.Pkg.Serv stats;
String stringName; // cached toString() output
final ApplicationInfo appInfo; // information about BackupAgent's app
final int backupMode; // full backup / incremental / restore
@@ -36,9 +33,7 @@ final class BackupRecord {
// ----- Implementation -----
- BackupRecord(BatteryStatsImpl.Uid.Pkg.Serv _agentStats, ApplicationInfo _appInfo,
- int _backupMode) {
- stats = _agentStats;
+ BackupRecord(ApplicationInfo _appInfo, int _backupMode) {
appInfo = _appInfo;
backupMode = _backupMode;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 18bed54d42c1..59c4697e4f1e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,6 +24,7 @@ import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
@@ -20034,6 +20035,11 @@ public class PackageManagerService extends IPackageManager.Stub
if (Process.isIsolated(uid)) {
return Zygote.MOUNT_EXTERNAL_NONE;
}
+ if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ return checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED
+ ? Zygote.MOUNT_EXTERNAL_FULL
+ : Zygote.MOUNT_EXTERNAL_WRITE;
+ }
if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
return Zygote.MOUNT_EXTERNAL_DEFAULT;
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index a78cb3360a4a..5befc1f99cf3 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -171,8 +171,11 @@ public final class DefaultPermissionGrantPolicy {
@Deprecated
private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
static {
- STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
- STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ // STOPSHIP(b/112545973): remove once feature enabled by default
+ if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+ STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ }
}
private static final Set<String> MEDIA_AURAL_PERMISSIONS = new ArraySet<>();
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 2f189a6bcdb9..df97027da64f 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -16,20 +16,18 @@
package com.android.server.wm;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
+import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING;
+import static com.android.server.wm.ScreenRotationAnimationProto.STARTED;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
-import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING;
-import static com.android.server.wm.ScreenRotationAnimationProto.STARTED;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
-import android.os.IBinder;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -37,7 +35,6 @@ import android.view.DisplayInfo;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
@@ -268,6 +265,12 @@ class ScreenRotationAnimation {
.setSecure(isSecure)
.build();
+ // In case display bounds change, screenshot buffer and surface may mismatch so set a
+ // scaling mode.
+ SurfaceControl.Transaction t2 = new SurfaceControl.Transaction();
+ t2.setOverrideScalingMode(mSurfaceControl, Surface.SCALING_MODE_SCALE_TO_WINDOW);
+ t2.apply(true /* sync */);
+
// Capture a screenshot into the surface we just created.
final int displayId = display.getDisplayId();
final Surface surface = new Surface();
diff --git a/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
index 91e6bd6469d8..11f2b6118e24 100644
--- a/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
@@ -24,10 +24,17 @@ import java.nio.ByteBuffer;
*/
class DhcpDiscoverPacket extends DhcpPacket {
/**
+ * The IP address of the client which sent this packet.
+ */
+ final Inet4Address mSrcIp;
+
+ /**
* Generates a DISCOVER packet with the specified parameters.
*/
- DhcpDiscoverPacket(int transId, short secs, byte[] clientMac, boolean broadcast) {
- super(transId, secs, INADDR_ANY, INADDR_ANY, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
+ DhcpDiscoverPacket(int transId, short secs, Inet4Address relayIp, byte[] clientMac,
+ boolean broadcast, Inet4Address srcIp) {
+ super(transId, secs, INADDR_ANY, INADDR_ANY, INADDR_ANY, relayIp, clientMac, broadcast);
+ mSrcIp = srcIp;
}
public String toString() {
@@ -41,8 +48,8 @@ class DhcpDiscoverPacket extends DhcpPacket {
*/
public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- fillInPacket(encap, INADDR_BROADCAST, INADDR_ANY, destUdp,
- srcUdp, result, DHCP_BOOTREQUEST, mBroadcast);
+ fillInPacket(encap, INADDR_BROADCAST, mSrcIp, destUdp, srcUdp, result, DHCP_BOOTREQUEST,
+ mBroadcast);
result.flip();
return result;
}
diff --git a/services/net/java/android/net/dhcp/DhcpLease.java b/services/net/java/android/net/dhcp/DhcpLease.java
new file mode 100644
index 000000000000..d2a15b37dcc0
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpLease.java
@@ -0,0 +1,138 @@
+/*
+ * 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 android.net.dhcp;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.MacAddress;
+import android.os.SystemClock;
+import android.text.TextUtils;
+
+import com.android.internal.util.HexDump;
+
+import java.net.Inet4Address;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * An IPv4 address assignment done through DHCPv4.
+ * @hide
+ */
+public class DhcpLease {
+ public static final long EXPIRATION_NEVER = Long.MAX_VALUE;
+ public static final String HOSTNAME_NONE = null;
+
+ @Nullable
+ private final byte[] mClientId;
+ @NonNull
+ private final MacAddress mHwAddr;
+ @NonNull
+ private final Inet4Address mNetAddr;
+ /**
+ * Expiration time for the lease, to compare with {@link SystemClock#elapsedRealtime()}.
+ */
+ private final long mExpTime;
+ @Nullable
+ private final String mHostname;
+
+ public DhcpLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
+ @NonNull Inet4Address netAddr, long expTime, @Nullable String hostname) {
+ mClientId = (clientId == null ? null : Arrays.copyOf(clientId, clientId.length));
+ mHwAddr = hwAddr;
+ mNetAddr = netAddr;
+ mExpTime = expTime;
+ mHostname = hostname;
+ }
+
+ @Nullable
+ public byte[] getClientId() {
+ if (mClientId == null) {
+ return null;
+ }
+ return Arrays.copyOf(mClientId, mClientId.length);
+ }
+
+ @NonNull
+ public MacAddress getHwAddr() {
+ return mHwAddr;
+ }
+
+ @Nullable
+ public String getHostname() {
+ return mHostname;
+ }
+
+ @NonNull
+ public Inet4Address getNetAddr() {
+ return mNetAddr;
+ }
+
+ public long getExpTime() {
+ return mExpTime;
+ }
+
+ /**
+ * Push back the expiration time of this lease. If the provided time is sooner than the original
+ * expiration time, the lease time will not be updated.
+ *
+ * <p>The lease hostname is updated with the provided one if set.
+ * @return A {@link DhcpLease} with expiration time set to max(expTime, currentExpTime)
+ */
+ public DhcpLease renewedLease(long expTime, @Nullable String hostname) {
+ return new DhcpLease(mClientId, mHwAddr, mNetAddr, Math.max(expTime, mExpTime),
+ (hostname == null ? mHostname : hostname));
+ }
+
+ public boolean matchesClient(@Nullable byte[] clientId, @NonNull MacAddress hwAddr) {
+ if (mClientId != null) {
+ return Arrays.equals(mClientId, clientId);
+ } else {
+ return clientId == null && mHwAddr.equals(hwAddr);
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof DhcpLease)) {
+ return false;
+ }
+ final DhcpLease other = (DhcpLease)obj;
+ return Arrays.equals(mClientId, other.mClientId)
+ && mHwAddr.equals(other.mHwAddr)
+ && mNetAddr.equals(other.mNetAddr)
+ && mExpTime == other.mExpTime
+ && TextUtils.equals(mHostname, other.mHostname);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mClientId, mHwAddr, mNetAddr, mHostname, mExpTime);
+ }
+
+ static String clientIdToString(byte[] bytes) {
+ if (bytes == null) {
+ return "null";
+ }
+ return HexDump.toHexString(bytes);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("clientId: %s, hwAddr: %s, netAddr: %s, expTime: %d, hostname: %s",
+ clientIdToString(mClientId), mHwAddr.toString(), mNetAddr, mExpTime, mHostname);
+ }
+}
diff --git a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java b/services/net/java/android/net/dhcp/DhcpLeaseRepository.java
new file mode 100644
index 000000000000..7e57c9f46350
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpLeaseRepository.java
@@ -0,0 +1,538 @@
+/*
+ * 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 android.net.dhcp;
+
+import static android.net.NetworkUtils.inet4AddressToIntHTH;
+import static android.net.NetworkUtils.intToInet4AddressHTH;
+import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTH;
+import static android.net.dhcp.DhcpLease.EXPIRATION_NEVER;
+import static android.net.util.NetworkConstants.IPV4_ADDR_BITS;
+
+import static java.lang.Math.min;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.IpPrefix;
+import android.net.MacAddress;
+import android.net.util.SharedLog;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+
+import java.net.Inet4Address;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.Function;
+
+/**
+ * A repository managing IPv4 address assignments through DHCPv4.
+ *
+ * <p>This class is not thread-safe. All public methods should be called on a common thread or
+ * use some synchronization mechanism.
+ *
+ * <p>Methods are optimized for a small number of allocated leases, assuming that most of the time
+ * only 2~10 addresses will be allocated, which is the common case. Managing a large number of
+ * addresses is supported but will be slower: some operations have complexity in O(num_leases).
+ * @hide
+ */
+class DhcpLeaseRepository {
+ public static final byte[] CLIENTID_UNSPEC = null;
+ public static final Inet4Address INETADDR_UNSPEC = null;
+
+ @NonNull
+ private final SharedLog mLog;
+ @NonNull
+ private final Clock mClock;
+
+ @NonNull
+ private IpPrefix mPrefix;
+ @NonNull
+ private Set<Inet4Address> mReservedAddrs;
+ private int mSubnetAddr;
+ private int mSubnetMask;
+ private int mNumAddresses;
+ private long mLeaseTimeMs;
+
+ public static class Clock {
+ /**
+ * @see SystemClock#elapsedRealtime()
+ */
+ public long elapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+ }
+
+ /**
+ * Next timestamp when committed or declined leases should be checked for expired ones. This
+ * will always be lower than or equal to the time for the first lease to expire: it's OK not to
+ * update this when removing entries, but it must always be updated when adding/updating.
+ */
+ private long mNextExpirationCheck = EXPIRATION_NEVER;
+
+ static class DhcpLeaseException extends Exception {
+ DhcpLeaseException(String message) {
+ super(message);
+ }
+ }
+
+ static class OutOfAddressesException extends DhcpLeaseException {
+ OutOfAddressesException(String message) {
+ super(message);
+ }
+ }
+
+ static class InvalidAddressException extends DhcpLeaseException {
+ InvalidAddressException(String message) {
+ super(message);
+ }
+ }
+
+ /**
+ * Leases by IP address
+ */
+ private final ArrayMap<Inet4Address, DhcpLease> mCommittedLeases = new ArrayMap<>();
+
+ /**
+ * Map address -> expiration timestamp in ms. Addresses are guaranteed to be valid as defined
+ * by {@link #isValidAddress(Inet4Address)}, but are not necessarily otherwise available for
+ * assignment.
+ */
+ private final LinkedHashMap<Inet4Address, Long> mDeclinedAddrs = new LinkedHashMap<>();
+
+ public DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
+ long leaseTimeMs, @NonNull SharedLog log, @NonNull Clock clock) {
+ updateParams(prefix, reservedAddrs, leaseTimeMs);
+ mLog = log;
+ mClock = clock;
+ }
+
+ public void updateParams(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
+ long leaseTimeMs) {
+ mPrefix = prefix;
+ mReservedAddrs = Collections.unmodifiableSet(new HashSet<>(reservedAddrs));
+ mSubnetMask = prefixLengthToV4NetmaskIntHTH(prefix.getPrefixLength());
+ mSubnetAddr = inet4AddressToIntHTH((Inet4Address) prefix.getAddress()) & mSubnetMask;
+ mNumAddresses = 1 << (IPV4_ADDR_BITS - prefix.getPrefixLength());
+ mLeaseTimeMs = leaseTimeMs;
+
+ cleanMap(mCommittedLeases);
+ cleanMap(mDeclinedAddrs);
+ }
+
+ /**
+ * From a map keyed by {@link Inet4Address}, remove entries where the key is invalid (as
+ * specified by {@link #isValidAddress(Inet4Address)}), or is a reserved address.
+ */
+ private <T> void cleanMap(Map<Inet4Address, T> map) {
+ final Iterator<Entry<Inet4Address, T>> it = map.entrySet().iterator();
+ while (it.hasNext()) {
+ final Inet4Address addr = it.next().getKey();
+ if (!isValidAddress(addr) || mReservedAddrs.contains(addr)) {
+ it.remove();
+ }
+ }
+ }
+
+ /**
+ * Get a DHCP offer, to reply to a DHCPDISCOVER. Follows RFC2131 #4.3.1.
+ *
+ * @param clientId Client identifier option if specified, or {@link #CLIENTID_UNSPEC}
+ * @param relayAddr Internet address of the relay (giaddr), can be {@link Inet4Address#ANY}
+ * @param reqAddr Requested address by the client (option 50), or {@link #INETADDR_UNSPEC}
+ * @param hostname Client-provided hostname, or {@link DhcpLease#HOSTNAME_NONE}
+ * @throws OutOfAddressesException The server does not have any available address
+ * @throws InvalidAddressException The lease was requested from an unsupported subnet
+ */
+ @NonNull
+ public DhcpLease getOffer(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
+ @NonNull Inet4Address relayAddr,
+ @Nullable Inet4Address reqAddr, @Nullable String hostname)
+ throws OutOfAddressesException, InvalidAddressException {
+ final long currentTime = mClock.elapsedRealtime();
+ final long expTime = currentTime + mLeaseTimeMs;
+
+ removeExpiredLeases(currentTime);
+
+ // As per #4.3.1, addresses are assigned based on the relay address if present. This
+ // implementation only assigns addresses if the relayAddr is inside our configured subnet.
+ // This also applies when the client requested a specific address for consistency between
+ // requests, and with older behavior.
+ if (isIpAddrOutsidePrefix(mPrefix, relayAddr)) {
+ throw new InvalidAddressException("Lease requested by relay from outside of subnet");
+ }
+
+ final DhcpLease currentLease = findByClient(clientId, hwAddr);
+ final DhcpLease newLease;
+ if (currentLease != null) {
+ newLease = currentLease.renewedLease(expTime, hostname);
+ mLog.log("Offering extended lease " + newLease);
+ // Do not update lease time in the map: the offer is not committed yet.
+ } else if (reqAddr != null && isValidAddress(reqAddr) && isAvailable(reqAddr)) {
+ newLease = new DhcpLease(clientId, hwAddr, reqAddr, expTime, hostname);
+ mLog.log("Offering requested lease " + newLease);
+ } else {
+ newLease = makeNewOffer(clientId, hwAddr, expTime, hostname);
+ mLog.log("Offering new generated lease " + newLease);
+ }
+ return newLease;
+ }
+
+ private static boolean isIpAddrOutsidePrefix(IpPrefix prefix, Inet4Address addr) {
+ return addr != null && !addr.equals(Inet4Address.ANY) && !prefix.contains(addr);
+ }
+
+ @Nullable
+ private DhcpLease findByClient(@Nullable byte[] clientId, @NonNull MacAddress hwAddr) {
+ for (DhcpLease lease : mCommittedLeases.values()) {
+ if (lease.matchesClient(clientId, hwAddr)) {
+ return lease;
+ }
+ }
+
+ // Note this differs from dnsmasq behavior, which would match by hwAddr if clientId was
+ // given but no lease keyed on clientId matched. This would prevent one interface from
+ // obtaining multiple leases with different clientId.
+ return null;
+ }
+
+ /**
+ * Make a lease conformant to a client DHCPREQUEST or renew the client's existing lease,
+ * commit it to the repository and return it.
+ *
+ * <p>This method always succeeds and commits the lease if it does not throw, and has no side
+ * effects if it throws.
+ *
+ * @param clientId Client identifier option if specified, or {@link #CLIENTID_UNSPEC}
+ * @param reqAddr Requested address by the client (option 50), or {@link #INETADDR_UNSPEC}
+ * @param sidSet Whether the server identifier was set in the request
+ * @return The newly created or renewed lease
+ * @throws InvalidAddressException The client provided an address that conflicts with its
+ * current configuration, or other committed/reserved leases.
+ */
+ @NonNull
+ public DhcpLease requestLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
+ @NonNull Inet4Address clientAddr, @Nullable Inet4Address reqAddr, boolean sidSet,
+ @Nullable String hostname) throws InvalidAddressException {
+ final long currentTime = mClock.elapsedRealtime();
+ removeExpiredLeases(currentTime);
+ final DhcpLease assignedLease = findByClient(clientId, hwAddr);
+
+ final Inet4Address leaseAddr = reqAddr != null ? reqAddr : clientAddr;
+ if (assignedLease != null) {
+ if (sidSet && reqAddr != null) {
+ // Client in SELECTING state; remove any current lease before creating a new one.
+ mCommittedLeases.remove(assignedLease.getNetAddr());
+ } else if (!assignedLease.getNetAddr().equals(leaseAddr)) {
+ // reqAddr null (RENEWING/REBINDING): client renewing its own lease for clientAddr.
+ // reqAddr set with sid not set (INIT-REBOOT): client verifying configuration.
+ // In both cases, throw if clientAddr or reqAddr does not match the known lease.
+ throw new InvalidAddressException("Incorrect address for client in " +
+ (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING"));
+ }
+ }
+
+ // In the init-reboot case, RFC2131 #4.3.2 says that the server must not reply if
+ // assignedLease == null, but dnsmasq will let the client use the requested address if
+ // available, when configured with --dhcp-authoritative. This is preferable to avoid issues
+ // if the server lost the lease DB: the client would not get a reply because the server
+ // does not know their lease.
+ // Similarly in RENEWING/REBINDING state, create a lease when possible if the
+ // client-provided lease is unknown.
+ final DhcpLease lease =
+ checkClientAndMakeLease(clientId, hwAddr, leaseAddr, hostname, currentTime);
+ mLog.logf("DHCPREQUEST assignedLease %s, reqAddr=%s, sidSet=%s: created/renewed lease %s",
+ assignedLease, reqAddr, sidSet, lease);
+ return lease;
+ }
+
+ /**
+ * Check that the client can request the specified address, make or renew the lease if yes, and
+ * commit it.
+ *
+ * <p>This method always succeeds and returns the lease if it does not throw, and has no
+ * side-effect if it throws.
+ *
+ * @return The newly created or renewed, committed lease
+ * @throws InvalidAddressException The client provided an address that conflicts with its
+ * current configuration, or other committed/reserved leases.
+ */
+ private DhcpLease checkClientAndMakeLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
+ @NonNull Inet4Address addr, @Nullable String hostname, long currentTime)
+ throws InvalidAddressException {
+ final long expTime = currentTime + mLeaseTimeMs;
+ final DhcpLease currentLease = mCommittedLeases.getOrDefault(addr, null);
+ if (currentLease != null && !currentLease.matchesClient(clientId, hwAddr)) {
+ throw new InvalidAddressException("Address in use");
+ }
+
+ final DhcpLease lease;
+ if (currentLease == null) {
+ if (isValidAddress(addr) && !mReservedAddrs.contains(addr)) {
+ lease = new DhcpLease(clientId, hwAddr, addr, expTime, hostname);
+ } else {
+ throw new InvalidAddressException("Lease not found and address unavailable");
+ }
+ } else {
+ lease = currentLease.renewedLease(expTime, hostname);
+ }
+ commitLease(lease);
+ return lease;
+ }
+
+ private void commitLease(@NonNull DhcpLease lease) {
+ mCommittedLeases.put(lease.getNetAddr(), lease);
+ maybeUpdateEarliestExpiration(lease.getExpTime());
+ }
+
+ /**
+ * Delete a committed lease from the repository.
+ *
+ * @return true if a lease matching parameters was found.
+ */
+ public boolean releaseLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
+ @NonNull Inet4Address addr) {
+ final DhcpLease currentLease = mCommittedLeases.getOrDefault(addr, null);
+ if (currentLease == null) {
+ mLog.w("Could not release unknown lease for " + addr);
+ return false;
+ }
+ if (currentLease.matchesClient(clientId, hwAddr)) {
+ mCommittedLeases.remove(addr);
+ mLog.log("Released lease " + currentLease);
+ return true;
+ }
+ mLog.w(String.format("Not releasing lease %s: does not match client (cid %s, hwAddr %s)",
+ currentLease, DhcpLease.clientIdToString(clientId), hwAddr));
+ return false;
+ }
+
+ public void markLeaseDeclined(@NonNull Inet4Address addr) {
+ if (mDeclinedAddrs.containsKey(addr) || !isValidAddress(addr)) {
+ mLog.logf("Not marking %s as declined: already declined or not assignable", addr);
+ return;
+ }
+ final long expTime = mClock.elapsedRealtime() + mLeaseTimeMs;
+ mDeclinedAddrs.put(addr, expTime);
+ mLog.logf("Marked %s as declined expiring %d", addr, expTime);
+ maybeUpdateEarliestExpiration(expTime);
+ }
+
+ /**
+ * Get the list of currently valid committed leases in the repository.
+ */
+ @NonNull
+ public List<DhcpLease> getCommittedLeases() {
+ removeExpiredLeases(mClock.elapsedRealtime());
+ return new ArrayList<>(mCommittedLeases.values());
+ }
+
+ /**
+ * Get the set of addresses that have been marked as declined in the repository.
+ */
+ @NonNull
+ public Set<Inet4Address> getDeclinedAddresses() {
+ removeExpiredLeases(mClock.elapsedRealtime());
+ return new HashSet<>(mDeclinedAddrs.keySet());
+ }
+
+ /**
+ * Given the expiration time of a new committed lease or declined address, update
+ * {@link #mNextExpirationCheck} so it stays lower than or equal to the time for the first lease
+ * to expire.
+ */
+ private void maybeUpdateEarliestExpiration(long expTime) {
+ if (expTime < mNextExpirationCheck) {
+ mNextExpirationCheck = expTime;
+ }
+ }
+
+ /**
+ * Remove expired entries from a map keyed by {@link Inet4Address}.
+ *
+ * @param tag Type of lease in the map, for logging
+ * @param getExpTime Functor returning the expiration time for an object in the map.
+ * Must not return null.
+ * @return The lowest expiration time among entries remaining in the map
+ */
+ private <T> long removeExpired(long currentTime, @NonNull Map<Inet4Address, T> map,
+ @NonNull String tag, @NonNull Function<T, Long> getExpTime) {
+ final Iterator<Entry<Inet4Address, T>> it = map.entrySet().iterator();
+ long firstExpiration = EXPIRATION_NEVER;
+ while (it.hasNext()) {
+ final Entry<Inet4Address, T> lease = it.next();
+ final long expTime = getExpTime.apply(lease.getValue());
+ if (expTime <= currentTime) {
+ mLog.logf("Removing expired %s lease for %s (expTime=%s, currentTime=%s)",
+ tag, lease.getKey(), expTime, currentTime);
+ it.remove();
+ } else {
+ firstExpiration = min(firstExpiration, expTime);
+ }
+ }
+ return firstExpiration;
+ }
+
+ /**
+ * Go through committed and declined leases and remove the expired ones.
+ */
+ private void removeExpiredLeases(long currentTime) {
+ if (currentTime < mNextExpirationCheck) {
+ return;
+ }
+
+ final long commExp = removeExpired(
+ currentTime, mCommittedLeases, "committed", DhcpLease::getExpTime);
+ final long declExp = removeExpired(
+ currentTime, mDeclinedAddrs, "declined", Function.identity());
+
+ mNextExpirationCheck = min(commExp, declExp);
+ }
+
+ private boolean isAvailable(@NonNull Inet4Address addr) {
+ return !mReservedAddrs.contains(addr) && !mCommittedLeases.containsKey(addr);
+ }
+
+ /**
+ * Get the 0-based index of an address in the subnet.
+ *
+ * <p>Given ordering of addresses 5.6.7.8 < 5.6.7.9 < 5.6.8.0, the index on a subnet is defined
+ * so that the first address is 0, the second 1, etc. For example on a /16, 192.168.0.0 -> 0,
+ * 192.168.0.1 -> 1, 192.168.1.0 -> 256
+ *
+ */
+ private int getAddrIndex(int addr) {
+ return addr & ~mSubnetMask;
+ }
+
+ private int getAddrByIndex(int index) {
+ return mSubnetAddr | index;
+ }
+
+ /**
+ * Get a valid address starting from the supplied one.
+ *
+ * <p>This only checks that the address is numerically valid for assignment, not whether it is
+ * already in use. The return value is always inside the configured prefix, even if the supplied
+ * address is not.
+ *
+ * <p>If the provided address is valid, it is returned as-is. Otherwise, the next valid
+ * address (with the ordering in {@link #getAddrIndex(int)}) is returned.
+ */
+ private int getValidAddress(int addr) {
+ final int lastByteMask = 0xff;
+ int addrIndex = getAddrIndex(addr); // 0-based index of the address in the subnet
+
+ // Some OSes do not handle addresses in .255 or .0 correctly: avoid those.
+ final int lastByte = getAddrByIndex(addrIndex) & lastByteMask;
+ if (lastByte == lastByteMask) {
+ // Avoid .255 address, and .0 address that follows
+ addrIndex = (addrIndex + 2) % mNumAddresses;
+ } else if (lastByte == 0) {
+ // Avoid .0 address
+ addrIndex = (addrIndex + 1) % mNumAddresses;
+ }
+
+ // Do not use first or last address of range
+ if (addrIndex == 0 || addrIndex == mNumAddresses - 1) {
+ // Always valid and not end of range since prefixLength is at most 30 in serving params
+ addrIndex = 1;
+ }
+ return getAddrByIndex(addrIndex);
+ }
+
+ /**
+ * Returns whether the address is in the configured subnet and part of the assignable range.
+ */
+ private boolean isValidAddress(Inet4Address addr) {
+ final int intAddr = inet4AddressToIntHTH(addr);
+ return getValidAddress(intAddr) == intAddr;
+ }
+
+ private int getNextAddress(int addr) {
+ final int addrIndex = getAddrIndex(addr);
+ final int nextAddress = getAddrByIndex((addrIndex + 1) % mNumAddresses);
+ return getValidAddress(nextAddress);
+ }
+
+ /**
+ * Calculate a first candidate address for a client by hashing the hardware address.
+ *
+ * <p>This will be a valid address as checked by {@link #getValidAddress(int)}, but may be
+ * in use.
+ *
+ * @return An IPv4 address encoded as 32-bit int
+ */
+ private int getFirstClientAddress(MacAddress hwAddr) {
+ // This follows dnsmasq behavior. Advantages are: clients will often get the same
+ // offers for different DISCOVER even if the lease was not yet accepted or has expired,
+ // and address generation will generally not need to loop through many allocated addresses
+ // until it finds a free one.
+ int hash = 0;
+ for (byte b : hwAddr.toByteArray()) {
+ hash += b + (b << 8) + (b << 16);
+ }
+ // This implementation will not always result in the same IPs as dnsmasq would give out in
+ // Android <= P, because it includes invalid and reserved addresses in mNumAddresses while
+ // the configured ranges for dnsmasq did not.
+ final int addrIndex = hash % mNumAddresses;
+ return getValidAddress(getAddrByIndex(addrIndex));
+ }
+
+ /**
+ * Create a lease that can be offered to respond to a client DISCOVER.
+ *
+ * <p>This method always succeeds and returns the lease if it does not throw. If no non-declined
+ * address is available, it will try to offer the oldest declined address if valid.
+ *
+ * @throws OutOfAddressesException The server has no address left to offer
+ */
+ private DhcpLease makeNewOffer(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
+ long expTime, @Nullable String hostname) throws OutOfAddressesException {
+ int intAddr = getFirstClientAddress(hwAddr);
+ // Loop until a free address is found, or there are no more addresses.
+ // There is slightly less than this many usable addresses, but some extra looping is OK
+ for (int i = 0; i < mNumAddresses; i++) {
+ final Inet4Address addr = intToInet4AddressHTH(intAddr);
+ if (isAvailable(addr) && !mDeclinedAddrs.containsKey(addr)) {
+ return new DhcpLease(clientId, hwAddr, addr, expTime, hostname);
+ }
+ intAddr = getNextAddress(intAddr);
+ }
+
+ // Try freeing DECLINEd addresses if out of addresses.
+ final Iterator<Inet4Address> it = mDeclinedAddrs.keySet().iterator();
+ while (it.hasNext()) {
+ final Inet4Address addr = it.next();
+ it.remove();
+ mLog.logf("Out of addresses in address pool: dropped declined addr %s", addr);
+ // isValidAddress() is always verified for entries in mDeclinedAddrs.
+ // However declined addresses may have been requested (typically by the machine that was
+ // already using the address) after being declined.
+ if (isAvailable(addr)) {
+ return new DhcpLease(clientId, hwAddr, addr, expTime, hostname);
+ }
+ }
+
+ throw new OutOfAddressesException("No address available for offer");
+ }
+}
diff --git a/services/net/java/android/net/dhcp/DhcpNakPacket.java b/services/net/java/android/net/dhcp/DhcpNakPacket.java
index 6458232b8fa0..ef9af527d063 100644
--- a/services/net/java/android/net/dhcp/DhcpNakPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpNakPacket.java
@@ -26,11 +26,9 @@ class DhcpNakPacket extends DhcpPacket {
/**
* Generates a NAK packet with the specified parameters.
*/
- DhcpNakPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
- Inet4Address nextIp, Inet4Address relayIp,
- byte[] clientMac) {
- super(transId, secs, INADDR_ANY, INADDR_ANY, nextIp, relayIp,
- clientMac, false);
+ DhcpNakPacket(int transId, short secs, Inet4Address nextIp, Inet4Address relayIp,
+ byte[] clientMac, boolean broadcast) {
+ super(transId, secs, INADDR_ANY, INADDR_ANY, nextIp, relayIp, clientMac, broadcast);
}
public String toString() {
@@ -43,11 +41,11 @@ class DhcpNakPacket extends DhcpPacket {
*/
public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- Inet4Address destIp = mClientIp;
- Inet4Address srcIp = mYourIp;
+ // Constructor does not set values for layers <= 3: use empty values
+ Inet4Address destIp = INADDR_ANY;
+ Inet4Address srcIp = INADDR_ANY;
- fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
- DHCP_BOOTREPLY, mBroadcast);
+ fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result, DHCP_BOOTREPLY, mBroadcast);
result.flip();
return result;
}
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index d90a4a235972..888821a8ad92 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -1,5 +1,6 @@
package android.net.dhcp;
+import android.annotation.Nullable;
import android.net.DhcpResults;
import android.net.LinkAddress;
import android.net.NetworkUtils;
@@ -204,6 +205,7 @@ public abstract class DhcpPacket {
protected static final byte DHCP_MESSAGE_TYPE_DECLINE = 4;
protected static final byte DHCP_MESSAGE_TYPE_ACK = 5;
protected static final byte DHCP_MESSAGE_TYPE_NAK = 6;
+ protected static final byte DHCP_MESSAGE_TYPE_RELEASE = 7;
protected static final byte DHCP_MESSAGE_TYPE_INFORM = 8;
/**
@@ -252,6 +254,7 @@ public abstract class DhcpPacket {
* DHCP Optional Type: DHCP Client Identifier
*/
protected static final byte DHCP_CLIENT_IDENTIFIER = 61;
+ protected byte[] mClientId;
/**
* DHCP zero-length option code: pad
@@ -281,7 +284,7 @@ public abstract class DhcpPacket {
protected final Inet4Address mClientIp;
protected final Inet4Address mYourIp;
private final Inet4Address mNextIp;
- private final Inet4Address mRelayIp;
+ protected final Inet4Address mRelayIp;
/**
* Does the client request a broadcast response?
@@ -338,13 +341,28 @@ public abstract class DhcpPacket {
return mClientMac;
}
+ // TODO: refactor DhcpClient to set clientId when constructing packets and remove
+ // hasExplicitClientId logic
/**
- * Returns the client ID. This follows RFC 2132 and is based on the hardware address.
+ * Returns whether a client ID was set in the options for this packet.
+ */
+ public boolean hasExplicitClientId() {
+ return mClientId != null;
+ }
+
+ /**
+ * Returns the client ID. If not set explicitly, this follows RFC 2132 and creates a client ID
+ * based on the hardware address.
*/
public byte[] getClientId() {
- byte[] clientId = new byte[mClientMac.length + 1];
- clientId[0] = CLIENT_ID_ETHER;
- System.arraycopy(mClientMac, 0, clientId, 1, mClientMac.length);
+ final byte[] clientId;
+ if (hasExplicitClientId()) {
+ clientId = Arrays.copyOf(mClientId, mClientId.length);
+ } else {
+ clientId = new byte[mClientMac.length + 1];
+ clientId[0] = CLIENT_ID_ETHER;
+ System.arraycopy(mClientMac, 0, clientId, 1, mClientMac.length);
+ }
return clientId;
}
@@ -531,8 +549,10 @@ public abstract class DhcpPacket {
/**
* Adds an optional parameter containing an array of bytes.
+ *
+ * <p>This method is a no-op if the payload argument is null.
*/
- protected static void addTlv(ByteBuffer buf, byte type, byte[] payload) {
+ protected static void addTlv(ByteBuffer buf, byte type, @Nullable byte[] payload) {
if (payload != null) {
if (payload.length > MAX_OPTION_LEN) {
throw new IllegalArgumentException("DHCP option too long: "
@@ -546,8 +566,10 @@ public abstract class DhcpPacket {
/**
* Adds an optional parameter containing an IP address.
+ *
+ * <p>This method is a no-op if the address argument is null.
*/
- protected static void addTlv(ByteBuffer buf, byte type, Inet4Address addr) {
+ protected static void addTlv(ByteBuffer buf, byte type, @Nullable Inet4Address addr) {
if (addr != null) {
addTlv(buf, type, addr.getAddress());
}
@@ -555,8 +577,10 @@ public abstract class DhcpPacket {
/**
* Adds an optional parameter containing a list of IP addresses.
+ *
+ * <p>This method is a no-op if the addresses argument is null or empty.
*/
- protected static void addTlv(ByteBuffer buf, byte type, List<Inet4Address> addrs) {
+ protected static void addTlv(ByteBuffer buf, byte type, @Nullable List<Inet4Address> addrs) {
if (addrs == null || addrs.size() == 0) return;
int optionLen = 4 * addrs.size();
@@ -574,9 +598,11 @@ public abstract class DhcpPacket {
}
/**
- * Adds an optional parameter containing a short integer
+ * Adds an optional parameter containing a short integer.
+ *
+ * <p>This method is a no-op if the value argument is null.
*/
- protected static void addTlv(ByteBuffer buf, byte type, Short value) {
+ protected static void addTlv(ByteBuffer buf, byte type, @Nullable Short value) {
if (value != null) {
buf.put(type);
buf.put((byte) 2);
@@ -585,9 +611,11 @@ public abstract class DhcpPacket {
}
/**
- * Adds an optional parameter containing a simple integer
+ * Adds an optional parameter containing a simple integer.
+ *
+ * <p>This method is a no-op if the value argument is null.
*/
- protected static void addTlv(ByteBuffer buf, byte type, Integer value) {
+ protected static void addTlv(ByteBuffer buf, byte type, @Nullable Integer value) {
if (value != null) {
buf.put(type);
buf.put((byte) 4);
@@ -597,12 +625,16 @@ public abstract class DhcpPacket {
/**
* Adds an optional parameter containing an ASCII string.
+ *
+ * <p>This method is a no-op if the string argument is null.
*/
- protected static void addTlv(ByteBuffer buf, byte type, String str) {
- try {
- addTlv(buf, type, str.getBytes("US-ASCII"));
- } catch (UnsupportedEncodingException e) {
- throw new IllegalArgumentException("String is not US-ASCII: " + str);
+ protected static void addTlv(ByteBuffer buf, byte type, @Nullable String str) {
+ if (str != null) {
+ try {
+ addTlv(buf, type, str.getBytes("US-ASCII"));
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalArgumentException("String is not US-ASCII: " + str);
+ }
}
}
@@ -740,6 +772,7 @@ public abstract class DhcpPacket {
Inet4Address nextIp;
Inet4Address relayIp;
byte[] clientMac;
+ byte[] clientId = null;
List<Inet4Address> dnsServers = new ArrayList<>();
List<Inet4Address> gateways = new ArrayList<>(); // aka router
Inet4Address serverIdentifier = null;
@@ -1038,8 +1071,8 @@ public abstract class DhcpPacket {
throw new ParseException(DhcpErrorEvent.DHCP_NO_MSG_TYPE,
"No DHCP message type option");
case DHCP_MESSAGE_TYPE_DISCOVER:
- newPacket = new DhcpDiscoverPacket(
- transactionId, secs, clientMac, broadcast);
+ newPacket = new DhcpDiscoverPacket(transactionId, secs, relayIp, clientMac,
+ broadcast, ipSrc);
break;
case DHCP_MESSAGE_TYPE_OFFER:
newPacket = new DhcpOfferPacket(
@@ -1047,7 +1080,7 @@ public abstract class DhcpPacket {
break;
case DHCP_MESSAGE_TYPE_REQUEST:
newPacket = new DhcpRequestPacket(
- transactionId, secs, clientIp, clientMac, broadcast);
+ transactionId, secs, clientIp, relayIp, clientMac, broadcast);
break;
case DHCP_MESSAGE_TYPE_DECLINE:
newPacket = new DhcpDeclinePacket(
@@ -1060,8 +1093,15 @@ public abstract class DhcpPacket {
break;
case DHCP_MESSAGE_TYPE_NAK:
newPacket = new DhcpNakPacket(
- transactionId, secs, clientIp, yourIp, nextIp, relayIp,
- clientMac);
+ transactionId, secs, nextIp, relayIp, clientMac, broadcast);
+ break;
+ case DHCP_MESSAGE_TYPE_RELEASE:
+ if (serverIdentifier == null) {
+ throw new ParseException(DhcpErrorEvent.MISC_ERROR,
+ "DHCPRELEASE without server identifier");
+ }
+ newPacket = new DhcpReleasePacket(
+ transactionId, serverIdentifier, clientIp, relayIp, clientMac);
break;
case DHCP_MESSAGE_TYPE_INFORM:
newPacket = new DhcpInformPacket(
@@ -1074,6 +1114,7 @@ public abstract class DhcpPacket {
}
newPacket.mBroadcastAddress = bcAddr;
+ newPacket.mClientId = clientId;
newPacket.mDnsServers = dnsServers;
newPacket.mDomainName = domainName;
newPacket.mGateways = gateways;
@@ -1173,8 +1214,8 @@ public abstract class DhcpPacket {
*/
public static ByteBuffer buildDiscoverPacket(int encap, int transactionId,
short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams) {
- DhcpPacket pkt = new DhcpDiscoverPacket(
- transactionId, secs, clientMac, broadcast);
+ DhcpPacket pkt = new DhcpDiscoverPacket(transactionId, secs, INADDR_ANY /* relayIp */,
+ clientMac, broadcast, INADDR_ANY /* srcIp */);
pkt.mRequestedParams = expectedParams;
return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
}
@@ -1223,12 +1264,11 @@ public abstract class DhcpPacket {
/**
* Builds a DHCP-NAK packet from the required specified parameters.
*/
- public static ByteBuffer buildNakPacket(int encap, int transactionId,
- Inet4Address serverIpAddr, Inet4Address clientIpAddr, byte[] mac) {
- DhcpPacket pkt = new DhcpNakPacket(transactionId, (short) 0, clientIpAddr,
- serverIpAddr, serverIpAddr, serverIpAddr, mac);
- pkt.mMessage = "requested address not available";
- pkt.mRequestedIp = clientIpAddr;
+ public static ByteBuffer buildNakPacket(int encap, int transactionId, Inet4Address serverIpAddr,
+ byte[] mac, boolean broadcast, String message) {
+ DhcpPacket pkt = new DhcpNakPacket(
+ transactionId, (short) 0, serverIpAddr, serverIpAddr, mac, broadcast);
+ pkt.mMessage = message;
return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
}
@@ -1240,7 +1280,7 @@ public abstract class DhcpPacket {
byte[] clientMac, Inet4Address requestedIpAddress,
Inet4Address serverIdentifier, byte[] requestedParams, String hostName) {
DhcpPacket pkt = new DhcpRequestPacket(transactionId, secs, clientIp,
- clientMac, broadcast);
+ INADDR_ANY /* relayIp */, clientMac, broadcast);
pkt.mRequestedIp = requestedIpAddress;
pkt.mServerIdentifier = serverIdentifier;
pkt.mHostName = hostName;
diff --git a/services/net/java/android/net/dhcp/DhcpPacketListener.java b/services/net/java/android/net/dhcp/DhcpPacketListener.java
new file mode 100644
index 000000000000..498fd93fff59
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpPacketListener.java
@@ -0,0 +1,84 @@
+/*
+ * 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 android.net.dhcp;
+
+import android.annotation.Nullable;
+import android.net.util.FdEventsReader;
+import android.net.util.PacketReader;
+import android.os.Handler;
+import android.system.Os;
+
+import java.io.FileDescriptor;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
+/**
+ * A {@link FdEventsReader} to receive and parse {@link DhcpPacket}.
+ * @hide
+ */
+abstract class DhcpPacketListener extends FdEventsReader<DhcpPacketListener.Payload> {
+ static final class Payload {
+ final byte[] bytes = new byte[DhcpPacket.MAX_LENGTH];
+ Inet4Address srcAddr;
+ }
+
+ public DhcpPacketListener(Handler handler) {
+ super(handler, new Payload());
+ }
+
+ @Override
+ protected int recvBufSize(Payload buffer) {
+ return buffer.bytes.length;
+ }
+
+ @Override
+ protected final void handlePacket(Payload recvbuf, int length) {
+ if (recvbuf.srcAddr == null) {
+ return;
+ }
+
+ try {
+ final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.bytes, length,
+ DhcpPacket.ENCAP_BOOTP);
+ onReceive(packet, recvbuf.srcAddr);
+ } catch (DhcpPacket.ParseException e) {
+ logParseError(recvbuf.bytes, length, e);
+ }
+ }
+
+ @Override
+ protected int readPacket(FileDescriptor fd, Payload packetBuffer) throws Exception {
+ final InetSocketAddress addr = new InetSocketAddress();
+ final int read = Os.recvfrom(
+ fd, packetBuffer.bytes, 0, packetBuffer.bytes.length, 0 /* flags */, addr);
+
+ // Buffers with null srcAddr will be dropped in handlePacket()
+ packetBuffer.srcAddr = inet4AddrOrNull(addr);
+ return read;
+ }
+
+ @Nullable
+ private static Inet4Address inet4AddrOrNull(InetSocketAddress addr) {
+ return addr.getAddress() instanceof Inet4Address
+ ? (Inet4Address) addr.getAddress()
+ : null;
+ }
+
+ protected abstract void onReceive(DhcpPacket packet, Inet4Address srcAddr);
+ protected abstract void logParseError(byte[] packet, int length, DhcpPacket.ParseException e);
+}
diff --git a/services/net/java/android/net/dhcp/DhcpReleasePacket.java b/services/net/java/android/net/dhcp/DhcpReleasePacket.java
new file mode 100644
index 000000000000..39583032c20d
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpReleasePacket.java
@@ -0,0 +1,58 @@
+/*
+ * 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 android.net.dhcp;
+
+import java.net.Inet4Address;
+import java.nio.ByteBuffer;
+
+/**
+ * Implements DHCP-RELEASE
+ */
+class DhcpReleasePacket extends DhcpPacket {
+
+ final Inet4Address mClientAddr;
+
+ /**
+ * Generates a RELEASE packet with the specified parameters.
+ */
+ public DhcpReleasePacket(int transId, Inet4Address serverId, Inet4Address clientAddr,
+ Inet4Address relayIp, byte[] clientMac) {
+ super(transId, (short)0, clientAddr, INADDR_ANY /* yourIp */, INADDR_ANY /* nextIp */,
+ relayIp, clientMac, false /* broadcast */);
+ mServerIdentifier = serverId;
+ mClientAddr = clientAddr;
+ }
+
+
+ @Override
+ public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
+ ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
+ fillInPacket(encap, mServerIdentifier /* destIp */, mClientIp /* srcIp */, destUdp, srcUdp,
+ result, DHCP_BOOTREPLY, mBroadcast);
+ result.flip();
+ return result;
+ }
+
+ @Override
+ void finishPacket(ByteBuffer buffer) {
+ addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_RELEASE);
+ addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
+ addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
+ addCommonClientTlvs(buffer);
+ addTlvEnd(buffer);
+ }
+}
diff --git a/services/net/java/android/net/dhcp/DhcpRequestPacket.java b/services/net/java/android/net/dhcp/DhcpRequestPacket.java
index 4f9aa01151ca..231d04576c28 100644
--- a/services/net/java/android/net/dhcp/DhcpRequestPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpRequestPacket.java
@@ -28,9 +28,9 @@ class DhcpRequestPacket extends DhcpPacket {
/**
* Generates a REQUEST packet with the specified parameters.
*/
- DhcpRequestPacket(int transId, short secs, Inet4Address clientIp, byte[] clientMac,
- boolean broadcast) {
- super(transId, secs, clientIp, INADDR_ANY, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
+ DhcpRequestPacket(int transId, short secs, Inet4Address clientIp, Inet4Address relayIp,
+ byte[] clientMac, boolean broadcast) {
+ super(transId, secs, clientIp, INADDR_ANY, INADDR_ANY, relayIp, clientMac, broadcast);
}
public String toString() {
diff --git a/services/net/java/android/net/dhcp/DhcpServingParams.java b/services/net/java/android/net/dhcp/DhcpServingParams.java
new file mode 100644
index 000000000000..ba9d116d0bd5
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpServingParams.java
@@ -0,0 +1,262 @@
+/*
+ * 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 android.net.dhcp;
+
+import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
+import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
+import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
+import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
+
+import static java.lang.Integer.toUnsignedLong;
+
+import android.annotation.NonNull;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.NetworkUtils;
+
+import java.net.Inet4Address;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Parameters used by the DhcpServer to serve requests.
+ *
+ * <p>Instances are immutable. Use {@link DhcpServingParams.Builder} to instantiate.
+ * @hide
+ */
+public class DhcpServingParams {
+ public static final int MTU_UNSET = 0;
+ public static final int MIN_PREFIX_LENGTH = 16;
+ public static final int MAX_PREFIX_LENGTH = 30;
+
+ /** Server inet address and prefix to serve */
+ @NonNull
+ public final LinkAddress serverAddr;
+
+ /**
+ * Default routers to be advertised to DHCP clients. May be empty.
+ * This set is provided by {@link DhcpServingParams.Builder} and is immutable.
+ */
+ @NonNull
+ public final Set<Inet4Address> defaultRouters;
+
+ /**
+ * DNS servers to be advertised to DHCP clients. May be empty.
+ * This set is provided by {@link DhcpServingParams.Builder} and is immutable.
+ */
+ @NonNull
+ public final Set<Inet4Address> dnsServers;
+
+ /**
+ * Excluded addresses that the DHCP server is not allowed to assign to clients.
+ * This set is provided by {@link DhcpServingParams.Builder} and is immutable.
+ */
+ @NonNull
+ public final Set<Inet4Address> excludedAddrs;
+
+ // DHCP uses uint32. Use long for clearer code, and check range when building.
+ public final long dhcpLeaseTimeSecs;
+ public final int linkMtu;
+
+ /**
+ * Checked exception thrown when some parameters used to build {@link DhcpServingParams} are
+ * missing or invalid.
+ */
+ public static class InvalidParameterException extends Exception {
+ public InvalidParameterException(String message) {
+ super(message);
+ }
+ }
+
+ private DhcpServingParams(@NonNull LinkAddress serverAddr,
+ @NonNull Set<Inet4Address> defaultRouters,
+ @NonNull Set<Inet4Address> dnsServers, @NonNull Set<Inet4Address> excludedAddrs,
+ long dhcpLeaseTimeSecs, int linkMtu) {
+ this.serverAddr = serverAddr;
+ this.defaultRouters = defaultRouters;
+ this.dnsServers = dnsServers;
+ this.excludedAddrs = excludedAddrs;
+ this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
+ this.linkMtu = linkMtu;
+ }
+
+ @NonNull
+ public Inet4Address getServerInet4Addr() {
+ return (Inet4Address) serverAddr.getAddress();
+ }
+
+ /**
+ * Get the served prefix mask as an IPv4 address.
+ *
+ * <p>For example, if the served prefix is 192.168.42.0/24, this will return 255.255.255.0.
+ */
+ @NonNull
+ public Inet4Address getPrefixMaskAsAddress() {
+ return getPrefixMaskAsInet4Address(serverAddr.getPrefixLength());
+ }
+
+ /**
+ * Get the server broadcast address.
+ *
+ * <p>For example, if the server {@link LinkAddress} is 192.168.42.1/24, this will return
+ * 192.168.42.255.
+ */
+ @NonNull
+ public Inet4Address getBroadcastAddress() {
+ return NetworkUtils.getBroadcastAddress(getServerInet4Addr(), serverAddr.getPrefixLength());
+ }
+
+ /**
+ * Utility class to create new instances of {@link DhcpServingParams} while checking validity
+ * of the parameters.
+ */
+ public static class Builder {
+ private LinkAddress serverAddr;
+ private Set<Inet4Address> defaultRouters;
+ private Set<Inet4Address> dnsServers;
+ private Set<Inet4Address> excludedAddrs;
+ private long dhcpLeaseTimeSecs;
+ private int linkMtu = MTU_UNSET;
+
+ /**
+ * Set the server address and served prefix for the DHCP server.
+ *
+ * <p>This parameter is required.
+ */
+ public Builder setServerAddr(@NonNull LinkAddress serverAddr) {
+ this.serverAddr = serverAddr;
+ return this;
+ }
+
+ /**
+ * Set the default routers to be advertised to DHCP clients.
+ *
+ * <p>Each router must be inside the served prefix. This may be an empty set, but it must
+ * always be set explicitly before building the {@link DhcpServingParams}.
+ */
+ public Builder setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) {
+ this.defaultRouters = defaultRouters;
+ return this;
+ }
+
+ /**
+ * Set the DNS servers to be advertised to DHCP clients.
+ *
+ * <p>This may be an empty set, but it must always be set explicitly before building the
+ * {@link DhcpServingParams}.
+ */
+ public Builder setDnsServers(@NonNull Set<Inet4Address> dnsServers) {
+ this.dnsServers = dnsServers;
+ return this;
+ }
+
+ /**
+ * Set excluded addresses that the DHCP server is not allowed to assign to clients.
+ *
+ * <p>This parameter is optional. DNS servers and default routers are always excluded
+ * and do not need to be set here.
+ */
+ public Builder setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) {
+ this.excludedAddrs = excludedAddrs;
+ return this;
+ }
+
+ /**
+ * Set the lease time for leases assigned by the DHCP server.
+ *
+ * <p>This parameter is required.
+ */
+ public Builder setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
+ this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
+ return this;
+ }
+
+ /**
+ * Set the link MTU to be advertised to DHCP clients.
+ *
+ * <p>If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter
+ * is optional and defaults to {@link #MTU_UNSET}.
+ */
+ public Builder setLinkMtu(int linkMtu) {
+ this.linkMtu = linkMtu;
+ return this;
+ }
+
+ /**
+ * Create a new {@link DhcpServingParams} instance based on parameters set in the builder.
+ *
+ * <p>This method has no side-effects. If it does not throw, a valid
+ * {@link DhcpServingParams} is returned.
+ * @return The constructed parameters.
+ * @throws InvalidParameterException At least one parameter is missing or invalid.
+ */
+ @NonNull
+ public DhcpServingParams build() throws InvalidParameterException {
+ if (serverAddr == null) {
+ throw new InvalidParameterException("Missing serverAddr");
+ }
+ if (defaultRouters == null) {
+ throw new InvalidParameterException("Missing defaultRouters");
+ }
+ if (dnsServers == null) {
+ // Empty set is OK, but enforce explicitly setting it
+ throw new InvalidParameterException("Missing dnsServers");
+ }
+ if (dhcpLeaseTimeSecs <= 0 || dhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) {
+ throw new InvalidParameterException("Invalid lease time: " + dhcpLeaseTimeSecs);
+ }
+ if (linkMtu != MTU_UNSET && (linkMtu < IPV4_MIN_MTU || linkMtu > IPV4_MAX_MTU)) {
+ throw new InvalidParameterException("Invalid link MTU: " + linkMtu);
+ }
+ if (!serverAddr.isIPv4()) {
+ throw new InvalidParameterException("serverAddr must be IPv4");
+ }
+ if (serverAddr.getPrefixLength() < MIN_PREFIX_LENGTH
+ || serverAddr.getPrefixLength() > MAX_PREFIX_LENGTH) {
+ throw new InvalidParameterException("Prefix length is not in supported range");
+ }
+
+ final IpPrefix prefix = makeIpPrefix(serverAddr);
+ for (Inet4Address addr : defaultRouters) {
+ if (!prefix.contains(addr)) {
+ throw new InvalidParameterException(String.format(
+ "Default router %s is not in server prefix %s", addr, serverAddr));
+ }
+ }
+
+ final Set<Inet4Address> excl = new HashSet<>();
+ if (excludedAddrs != null) {
+ excl.addAll(excludedAddrs);
+ }
+ excl.add((Inet4Address) serverAddr.getAddress());
+ excl.addAll(defaultRouters);
+ excl.addAll(dnsServers);
+
+ return new DhcpServingParams(serverAddr,
+ Collections.unmodifiableSet(new HashSet<>(defaultRouters)),
+ Collections.unmodifiableSet(new HashSet<>(dnsServers)),
+ Collections.unmodifiableSet(excl),
+ dhcpLeaseTimeSecs, linkMtu);
+ }
+ }
+
+ @NonNull
+ static IpPrefix makeIpPrefix(@NonNull LinkAddress addr) {
+ return new IpPrefix(addr.getAddress(), addr.getPrefixLength());
+ }
+}
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 7f821ffd369a..b77da28401d6 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -524,7 +524,7 @@ public class IpClient extends StateMachine {
return false;
}
// There no more than one IPv4 address
- if (ipAddresses.stream().filter(Inet4Address.class::isInstance).count() > 1) {
+ if (ipAddresses.stream().filter(LinkAddress::isIPv4).count() > 1) {
return false;
}
diff --git a/services/net/java/android/net/util/FdEventsReader.java b/services/net/java/android/net/util/FdEventsReader.java
new file mode 100644
index 000000000000..575444f89bc3
--- /dev/null
+++ b/services/net/java/android/net/util/FdEventsReader.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2016 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.net.util;
+
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.MessageQueue;
+import android.system.ErrnoException;
+import android.system.OsConstants;
+
+import libcore.io.IoUtils;
+
+import java.io.FileDescriptor;
+
+
+/**
+ * This class encapsulates the mechanics of registering a file descriptor
+ * with a thread's Looper and handling read events (and errors).
+ *
+ * Subclasses MUST implement createFd() and SHOULD override handlePacket(). They MAY override
+ * onStop() and onStart().
+ *
+ * Subclasses can expect a call life-cycle like the following:
+ *
+ * [1] when a client calls start(), createFd() is called, followed by the onStart() hook if all
+ * goes well. Implementations may override onStart() for additional initialization.
+ *
+ * [2] yield, waiting for read event or error notification:
+ *
+ * [a] readPacket() && handlePacket()
+ *
+ * [b] if (no error):
+ * goto 2
+ * else:
+ * goto 3
+ *
+ * [3] when a client calls stop(), the onStop() hook is called (unless already stopped or never
+ * started). Implementations may override onStop() for additional cleanup.
+ *
+ * The packet receive buffer is recycled on every read call, so subclasses
+ * should make any copies they would like inside their handlePacket()
+ * implementation.
+ *
+ * All public methods MUST only be called from the same thread with which
+ * the Handler constructor argument is associated.
+ *
+ * @hide
+ */
+public abstract class FdEventsReader<BufferType> {
+ private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
+ private static final int UNREGISTER_THIS_FD = 0;
+
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final MessageQueue mQueue;
+ @NonNull
+ private final BufferType mBuffer;
+ @Nullable
+ private FileDescriptor mFd;
+ private long mPacketsReceived;
+
+ protected static void closeFd(FileDescriptor fd) {
+ IoUtils.closeQuietly(fd);
+ }
+
+ protected FdEventsReader(@NonNull Handler h, @NonNull BufferType buffer) {
+ mHandler = h;
+ mQueue = mHandler.getLooper().getQueue();
+ mBuffer = buffer;
+ }
+
+ public final void start() {
+ if (onCorrectThread()) {
+ createAndRegisterFd();
+ } else {
+ mHandler.post(() -> {
+ logError("start() called from off-thread", null);
+ createAndRegisterFd();
+ });
+ }
+ }
+
+ public final void stop() {
+ if (onCorrectThread()) {
+ unregisterAndDestroyFd();
+ } else {
+ mHandler.post(() -> {
+ logError("stop() called from off-thread", null);
+ unregisterAndDestroyFd();
+ });
+ }
+ }
+
+ @NonNull
+ public Handler getHandler() { return mHandler; }
+
+ protected abstract int recvBufSize(@NonNull BufferType buffer);
+
+ public int recvBufSize() { return recvBufSize(mBuffer); }
+
+ /**
+ * Get the number of successful calls to {@link #readPacket(FileDescriptor, Object)}.
+ *
+ * <p>A call was successful if {@link #readPacket(FileDescriptor, Object)} returned a value > 0.
+ */
+ public final long numPacketsReceived() { return mPacketsReceived; }
+
+ /**
+ * Subclasses MUST create the listening socket here, including setting
+ * all desired socket options, interface or address/port binding, etc.
+ */
+ @Nullable
+ protected abstract FileDescriptor createFd();
+
+ /**
+ * Implementations MUST return the bytes read or throw an Exception.
+ *
+ * <p>The caller may throw a {@link ErrnoException} with {@link OsConstants#EAGAIN} or
+ * {@link OsConstants#EINTR}, in which case {@link FdEventsReader} will ignore the buffer
+ * contents and respectively wait for further input or retry the read immediately. For all other
+ * exceptions, the {@link FdEventsReader} will be stopped with no more interactions with this
+ * method.
+ */
+ protected abstract int readPacket(@NonNull FileDescriptor fd, @NonNull BufferType buffer)
+ throws Exception;
+
+ /**
+ * Called by the main loop for every packet. Any desired copies of
+ * |recvbuf| should be made in here, as the underlying byte array is
+ * reused across all reads.
+ */
+ protected void handlePacket(@NonNull BufferType recvbuf, int length) {}
+
+ /**
+ * Called by the main loop to log errors. In some cases |e| may be null.
+ */
+ protected void logError(@NonNull String msg, @Nullable Exception e) {}
+
+ /**
+ * Called by start(), if successful, just prior to returning.
+ */
+ protected void onStart() {}
+
+ /**
+ * Called by stop() just prior to returning.
+ */
+ protected void onStop() {}
+
+ private void createAndRegisterFd() {
+ if (mFd != null) return;
+
+ try {
+ mFd = createFd();
+ if (mFd != null) {
+ // Force the socket to be non-blocking.
+ IoUtils.setBlocking(mFd, false);
+ }
+ } catch (Exception e) {
+ logError("Failed to create socket: ", e);
+ closeFd(mFd);
+ mFd = null;
+ }
+
+ if (mFd == null) return;
+
+ mQueue.addOnFileDescriptorEventListener(
+ mFd,
+ FD_EVENTS,
+ (fd, events) -> {
+ // Always call handleInput() so read/recvfrom are given
+ // a proper chance to encounter a meaningful errno and
+ // perhaps log a useful error message.
+ if (!isRunning() || !handleInput()) {
+ unregisterAndDestroyFd();
+ return UNREGISTER_THIS_FD;
+ }
+ return FD_EVENTS;
+ });
+ onStart();
+ }
+
+ private boolean isRunning() { return (mFd != null) && mFd.valid(); }
+
+ // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error.
+ private boolean handleInput() {
+ while (isRunning()) {
+ final int bytesRead;
+
+ try {
+ bytesRead = readPacket(mFd, mBuffer);
+ if (bytesRead < 1) {
+ if (isRunning()) logError("Socket closed, exiting", null);
+ break;
+ }
+ mPacketsReceived++;
+ } catch (ErrnoException e) {
+ if (e.errno == OsConstants.EAGAIN) {
+ // We've read everything there is to read this time around.
+ return true;
+ } else if (e.errno == OsConstants.EINTR) {
+ continue;
+ } else {
+ if (isRunning()) logError("readPacket error: ", e);
+ break;
+ }
+ } catch (Exception e) {
+ if (isRunning()) logError("readPacket error: ", e);
+ break;
+ }
+
+ try {
+ handlePacket(mBuffer, bytesRead);
+ } catch (Exception e) {
+ logError("handlePacket error: ", e);
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ private void unregisterAndDestroyFd() {
+ if (mFd == null) return;
+
+ mQueue.removeOnFileDescriptorEventListener(mFd);
+ closeFd(mFd);
+ mFd = null;
+ onStop();
+ }
+
+ private boolean onCorrectThread() {
+ return (mHandler.getLooper() == Looper.myLooper());
+ }
+}
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index de04fd0ced04..3defe56939f5 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -77,10 +77,12 @@ public final class NetworkConstants {
/**
* IPv4 constants.
*
- * See als:
+ * See also:
* - https://tools.ietf.org/html/rfc791
*/
public static final int IPV4_HEADER_MIN_LEN = 20;
+ public static final int IPV4_MIN_MTU = 68;
+ public static final int IPV4_MAX_MTU = 65_535;
public static final int IPV4_IHL_MASK = 0xf;
public static final int IPV4_FLAGS_OFFSET = 6;
public static final int IPV4_FRAGMENT_MASK = 0x1fff;
diff --git a/services/net/java/android/net/util/PacketReader.java b/services/net/java/android/net/util/PacketReader.java
index 10da2a551e21..4aec6b6753a6 100644
--- a/services/net/java/android/net/util/PacketReader.java
+++ b/services/net/java/android/net/util/PacketReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -16,236 +16,46 @@
package android.net.util;
-import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
-import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
+import static java.lang.Math.max;
-import android.annotation.Nullable;
import android.os.Handler;
-import android.os.Looper;
-import android.os.MessageQueue;
-import android.os.MessageQueue.OnFileDescriptorEventListener;
-import android.system.ErrnoException;
import android.system.Os;
-import android.system.OsConstants;
-
-import libcore.io.IoUtils;
import java.io.FileDescriptor;
-import java.io.IOException;
-
/**
- * This class encapsulates the mechanics of registering a file descriptor
- * with a thread's Looper and handling read events (and errors).
- *
- * Subclasses MUST implement createFd() and SHOULD override handlePacket().
-
- * Subclasses can expect a call life-cycle like the following:
- *
- * [1] start() calls createFd() and (if all goes well) onStart()
- *
- * [2] yield, waiting for read event or error notification:
- *
- * [a] readPacket() && handlePacket()
- *
- * [b] if (no error):
- * goto 2
- * else:
- * goto 3
- *
- * [3] stop() calls onStop() if not previously stopped
- *
- * The packet receive buffer is recycled on every read call, so subclasses
- * should make any copies they would like inside their handlePacket()
- * implementation.
- *
- * All public methods MUST only be called from the same thread with which
- * the Handler constructor argument is associated.
+ * Specialization of {@link FdEventsReader} that reads packets into a byte array.
*
* TODO: rename this class to something more correctly descriptive (something
* like [or less horrible than] FdReadEventsHandler?).
*
* @hide
*/
-public abstract class PacketReader {
- private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
- private static final int UNREGISTER_THIS_FD = 0;
+public abstract class PacketReader extends FdEventsReader<byte[]> {
public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024;
- private final Handler mHandler;
- private final MessageQueue mQueue;
- private final byte[] mPacket;
- private FileDescriptor mFd;
- private long mPacketsReceived;
-
- protected static void closeFd(FileDescriptor fd) {
- IoUtils.closeQuietly(fd);
- }
-
protected PacketReader(Handler h) {
this(h, DEFAULT_RECV_BUF_SIZE);
}
- protected PacketReader(Handler h, int recvbufsize) {
- mHandler = h;
- mQueue = mHandler.getLooper().getQueue();
- mPacket = new byte[Math.max(recvbufsize, DEFAULT_RECV_BUF_SIZE)];
- }
-
- public final void start() {
- if (onCorrectThread()) {
- createAndRegisterFd();
- } else {
- mHandler.post(() -> {
- logError("start() called from off-thread", null);
- createAndRegisterFd();
- });
- }
+ protected PacketReader(Handler h, int recvBufSize) {
+ super(h, new byte[max(recvBufSize, DEFAULT_RECV_BUF_SIZE)]);
}
- public final void stop() {
- if (onCorrectThread()) {
- unregisterAndDestroyFd();
- } else {
- mHandler.post(() -> {
- logError("stop() called from off-thread", null);
- unregisterAndDestroyFd();
- });
- }
+ @Override
+ protected final int recvBufSize(byte[] buffer) {
+ return buffer.length;
}
- public Handler getHandler() { return mHandler; }
-
- public final int recvBufSize() { return mPacket.length; }
-
- public final long numPacketsReceived() { return mPacketsReceived; }
-
- /**
- * Subclasses MUST create the listening socket here, including setting
- * all desired socket options, interface or address/port binding, etc.
- */
- protected abstract FileDescriptor createFd();
-
/**
* Subclasses MAY override this to change the default read() implementation
* in favour of, say, recvfrom().
*
* Implementations MUST return the bytes read or throw an Exception.
*/
+ @Override
protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception {
return Os.read(fd, packetBuffer, 0, packetBuffer.length);
}
-
- /**
- * Called by the main loop for every packet. Any desired copies of
- * |recvbuf| should be made in here, as the underlying byte array is
- * reused across all reads.
- */
- protected void handlePacket(byte[] recvbuf, int length) {}
-
- /**
- * Called by the main loop to log errors. In some cases |e| may be null.
- */
- protected void logError(String msg, Exception e) {}
-
- /**
- * Called by start(), if successful, just prior to returning.
- */
- protected void onStart() {}
-
- /**
- * Called by stop() just prior to returning.
- */
- protected void onStop() {}
-
- private void createAndRegisterFd() {
- if (mFd != null) return;
-
- try {
- mFd = createFd();
- if (mFd != null) {
- // Force the socket to be non-blocking.
- IoUtils.setBlocking(mFd, false);
- }
- } catch (Exception e) {
- logError("Failed to create socket: ", e);
- closeFd(mFd);
- mFd = null;
- return;
- }
-
- if (mFd == null) return;
-
- mQueue.addOnFileDescriptorEventListener(
- mFd,
- FD_EVENTS,
- new OnFileDescriptorEventListener() {
- @Override
- public int onFileDescriptorEvents(FileDescriptor fd, int events) {
- // Always call handleInput() so read/recvfrom are given
- // a proper chance to encounter a meaningful errno and
- // perhaps log a useful error message.
- if (!isRunning() || !handleInput()) {
- unregisterAndDestroyFd();
- return UNREGISTER_THIS_FD;
- }
- return FD_EVENTS;
- }
- });
- onStart();
- }
-
- private boolean isRunning() { return (mFd != null) && mFd.valid(); }
-
- // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error.
- private boolean handleInput() {
- while (isRunning()) {
- final int bytesRead;
-
- try {
- bytesRead = readPacket(mFd, mPacket);
- if (bytesRead < 1) {
- if (isRunning()) logError("Socket closed, exiting", null);
- break;
- }
- mPacketsReceived++;
- } catch (ErrnoException e) {
- if (e.errno == OsConstants.EAGAIN) {
- // We've read everything there is to read this time around.
- return true;
- } else if (e.errno == OsConstants.EINTR) {
- continue;
- } else {
- if (isRunning()) logError("readPacket error: ", e);
- break;
- }
- } catch (Exception e) {
- if (isRunning()) logError("readPacket error: ", e);
- break;
- }
-
- try {
- handlePacket(mPacket, bytesRead);
- } catch (Exception e) {
- logError("handlePacket error: ", e);
- break;
- }
- }
-
- return false;
- }
-
- private void unregisterAndDestroyFd() {
- if (mFd == null) return;
-
- mQueue.removeOnFileDescriptorEventListener(mFd);
- closeFd(mFd);
- mFd = null;
- onStop();
- }
-
- private boolean onCorrectThread() {
- return (mHandler.getLooper() == Looper.myLooper());
- }
}
diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java
index bbd3d13efbd6..f7bf393f367b 100644
--- a/services/net/java/android/net/util/SharedLog.java
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -16,6 +16,7 @@
package android.net.util;
+import android.annotation.NonNull;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;
@@ -90,6 +91,13 @@ public class SharedLog {
Log.e(mTag, record(Category.ERROR, msg));
}
+ /**
+ * Log an error due to an exception, with the exception stacktrace.
+ */
+ public void e(@NonNull String msg, @NonNull Throwable e) {
+ Log.e(mTag, record(Category.ERROR, msg + ": " + e.getMessage()), e);
+ }
+
public void i(String msg) {
Log.i(mTag, record(Category.NONE, msg));
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 9d617a96e60f..48c89025e865 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -18,13 +18,13 @@ package com.android.server.hdmi;
import static com.google.common.truth.Truth.assertThat;
import android.annotation.Nullable;
+import android.app.Instrumentation;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
import android.os.test.TestLooper;
-
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -44,13 +44,16 @@ public class ArcTerminationActionFromAvrTest {
private boolean mShouldDispatchReportArcTerminated;
private boolean mArcEnabled;
private boolean mSetArcStatusCalled;
+ private Instrumentation mInstrumentation;
@Before
public void setUp() {
mDeviceInfoForTests = new HdmiDeviceInfo(1000, 1);
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+
HdmiControlService hdmiControlService =
- new HdmiControlService(null) {
+ new HdmiControlService(mInstrumentation.getTargetContext()) {
@Override
void sendCecCommand(
HdmiCecMessage command, @Nullable SendMessageCallback callback) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
index 81145103f689..3736df547b05 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -22,9 +22,8 @@ import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
import android.os.test.TestLooper;
-
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-
import com.android.server.hdmi.HdmiCecLocalDeviceAudioSystem.TvSystemAudioModeSupportedCallback;
import org.junit.Before;
@@ -50,7 +49,7 @@ public class DetectTvSystemAudioModeSupportActionTest {
public void SetUp() {
mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
HdmiControlService hdmiControlService =
- new HdmiControlService(null) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@Override
void sendCecCommand(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index cc005ed266b3..da840be9bca7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -18,7 +18,6 @@ package com.android.server.hdmi;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
-
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
@@ -26,18 +25,15 @@ import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_3;
import static com.android.server.hdmi.Constants.ADDR_SPECIFIC_USE;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
-
import static junit.framework.Assert.assertEquals;
import android.content.Context;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
import android.os.test.TestLooper;
-
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -84,7 +80,7 @@ public class HdmiCecControllerTest {
public void SetUp() {
mMyLooper = mTestLooper.getLooper();
mMyLooper = mTestLooper.getLooper();
- mHdmiControlService = new MyHdmiControlService(null);
+ mHdmiControlService = new MyHdmiControlService(InstrumentationRegistry.getTargetContext());
mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController =
HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 6dbbbfe8e9e4..0e15ba6d0f46 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -30,14 +30,13 @@ import android.media.AudioManager;
import android.os.Looper;
import android.os.SystemProperties;
import android.os.test.TestLooper;
-
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-
import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import org.junit.Before;
-import org.junit.Test;
import org.junit.Ignore;
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -48,7 +47,6 @@ import java.util.ArrayList;
/** Tests for {@link HdmiCecLocalDeviceAudioSystem} class. */
public class HdmiCecLocalDeviceAudioSystemTest {
- private static final String TAG = "HdmiCecLocalDeviceAudioSystemTest";
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
@@ -61,9 +59,9 @@ public class HdmiCecLocalDeviceAudioSystemTest {
private boolean mMusicMute;
@Before
- public void SetUp() {
+ public void setUp() {
mHdmiControlService =
- new HdmiControlService(null) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index daf35dd382fa..910af78b2d7e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -30,9 +30,9 @@ import static junit.framework.Assert.assertTrue;
import android.hardware.hdmi.HdmiControlManager;
import android.os.test.TestLooper;
-
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-
+import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -95,7 +95,7 @@ public class HdmiCecLocalDeviceTest {
@Before
public void SetUp() {
mHdmiControlService =
- new HdmiControlService(null) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@Override
boolean isControlEnabled() {
return isControlEnabled;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 71af71e711ea..18c9a653dc7d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -25,9 +25,8 @@ import static junit.framework.Assert.assertTrue;
import android.os.Looper;
import android.os.test.TestLooper;
-
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -107,7 +106,7 @@ public class HdmiControlServiceTest {
@Before
public void SetUp() {
mHdmiControlService =
- new HdmiControlService(null) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@Override
boolean isStandbyMessageReceived() {
return mStandbyMessageReceived;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index 6ff1c0f2cef1..d914b9a090a2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -26,9 +26,8 @@ import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
-
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,7 +54,7 @@ public class SystemAudioInitiationActionFromAvrTest {
public void SetUp() {
mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
HdmiControlService hdmiControlService =
- new HdmiControlService(null) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@Override
void sendCecCommand(
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index c240dbb684e6..76a002681710 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -129,6 +129,12 @@ public abstract class CellIdentity implements Parcelable {
return mAlphaShort;
}
+ /**
+ * @return a CellLocation object for this CellIdentity
+ * @hide
+ */
+ public abstract CellLocation asCellLocation();
+
@Override
public boolean equals(Object other) {
if (!(other instanceof CellIdentity)) {
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 5b67dc44fe3c..9218bdc31fa8 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -19,7 +19,7 @@ package android.telephony;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
-import android.text.TextUtils;
+import android.telephony.cdma.CdmaCellLocation;
import java.util.Objects;
@@ -178,6 +178,18 @@ public final class CellIdentityCdma extends CellIdentity {
super.hashCode());
}
+ /** @hide */
+ @Override
+ public CdmaCellLocation asCellLocation() {
+ CdmaCellLocation cl = new CdmaCellLocation();
+ int bsid = mBasestationId != Integer.MAX_VALUE ? mBasestationId : -1;
+ int sid = mSystemId != Integer.MAX_VALUE ? mSystemId : -1;
+ int nid = mNetworkId != Integer.MAX_VALUE ? mNetworkId : -1;
+ // lat and long already use Integer.MAX_VALUE for invalid/unknown
+ cl.setCellLocationData(bsid, mLatitude, mLongitude, sid, nid);
+ return cl;
+ }
+
@Override
public boolean equals(Object other) {
if (this == other) {
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 5c847914b57b..cb9dbf369d7e 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -19,6 +19,7 @@ package android.telephony;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
+import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
import java.util.Objects;
@@ -198,6 +199,17 @@ public final class CellIdentityGsm extends CellIdentity {
return Integer.MAX_VALUE;
}
+ /** @hide */
+ @Override
+ public GsmCellLocation asCellLocation() {
+ GsmCellLocation cl = new GsmCellLocation();
+ int lac = mLac != Integer.MAX_VALUE ? mLac : -1;
+ int cid = mCid != Integer.MAX_VALUE ? mCid : -1;
+ cl.setLacAndCid(lac, cid);
+ cl.setPsc(-1);
+ return cl;
+ }
+
@Override
public int hashCode() {
return Objects.hash(mLac, mCid, super.hashCode());
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 65c904b8548b..b44e891fa870 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -19,6 +19,7 @@ package android.telephony;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
+import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
import java.util.Objects;
@@ -200,6 +201,28 @@ public final class CellIdentityLte extends CellIdentity {
return mEarfcn;
}
+ /**
+ * A hack to allow tunneling of LTE information via GsmCellLocation
+ * so that older Network Location Providers can return some information
+ * on LTE only networks, see bug 9228974.
+ *
+ * The tunnel'd LTE information is returned as follows:
+ * LAC = TAC field
+ * CID = CI field
+ * PSC = 0.
+ *
+ * @hide
+ */
+ @Override
+ public GsmCellLocation asCellLocation() {
+ GsmCellLocation cl = new GsmCellLocation();
+ int tac = mTac != Integer.MAX_VALUE ? mTac : -1;
+ int cid = mCi != Integer.MAX_VALUE ? mCi : -1;
+ cl.setLacAndCid(tac, cid);
+ cl.setPsc(0);
+ return cl;
+ }
+
@Override
public int hashCode() {
return Objects.hash(mCi, mPci, mTac, super.hashCode());
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 21b9601bcad9..5a9e474806a3 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.os.Parcel;
+import android.telephony.gsm.GsmCellLocation;
import java.util.Objects;
@@ -134,6 +135,17 @@ public final class CellIdentityTdscdma extends CellIdentity {
return mUarfcn;
}
+ /** @hide */
+ @Override
+ public GsmCellLocation asCellLocation() {
+ GsmCellLocation cl = new GsmCellLocation();
+ int lac = mLac != Integer.MAX_VALUE ? mLac : -1;
+ int cid = mCid != Integer.MAX_VALUE ? mCid : -1;
+ cl.setLacAndCid(lac, cid);
+ cl.setPsc(-1); // There is no PSC for TD-SCDMA; not using this for CPI to stem shenanigans
+ return cl;
+ }
+
@Override
public boolean equals(Object other) {
if (this == other) {
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index e26fcb314afa..727d9908b9b1 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -19,6 +19,7 @@ package android.telephony;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
+import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
import java.util.Objects;
@@ -191,6 +192,19 @@ public final class CellIdentityWcdma extends CellIdentity {
return mUarfcn;
}
+ /** @hide */
+ @Override
+ public GsmCellLocation asCellLocation() {
+ GsmCellLocation cl = new GsmCellLocation();
+ int lac = mLac != Integer.MAX_VALUE ? mLac : -1;
+ int cid = mCid != Integer.MAX_VALUE ? mCid : -1;
+ int psc = mPsc != Integer.MAX_VALUE ? mPsc : -1;
+ cl.setLacAndCid(lac, cid);
+ cl.setPsc(psc);
+
+ return cl;
+ }
+
@Override
public boolean equals(Object other) {
if (this == other) {
diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
index 2b172dac4865..3452819835f5 100644
--- a/tests/net/java/android/net/NetworkUtilsTest.java
+++ b/tests/net/java/android/net/NetworkUtilsTest.java
@@ -24,6 +24,8 @@ import static android.net.NetworkUtils.intToInet4AddressHTL;
import static android.net.NetworkUtils.netmaskToPrefixLength;
import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTH;
import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTL;
+import static android.net.NetworkUtils.getBroadcastAddress;
+import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
import static junit.framework.Assert.assertEquals;
@@ -125,7 +127,6 @@ public class NetworkUtilsTest {
assertInvalidNetworkMask(IPv4Address("255.255.0.255"));
}
-
@Test
public void testPrefixLengthToV4NetmaskIntHTL() {
assertEquals(0, prefixLengthToV4NetmaskIntHTL(0));
@@ -266,4 +267,44 @@ public class NetworkUtilsTest {
assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536),
NetworkUtils.routedIPv6AddressCount(set));
}
+
+ @Test
+ public void testGetPrefixMaskAsAddress() {
+ assertEquals("255.255.240.0", getPrefixMaskAsInet4Address(20).getHostAddress());
+ assertEquals("255.0.0.0", getPrefixMaskAsInet4Address(8).getHostAddress());
+ assertEquals("0.0.0.0", getPrefixMaskAsInet4Address(0).getHostAddress());
+ assertEquals("255.255.255.255", getPrefixMaskAsInet4Address(32).getHostAddress());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetPrefixMaskAsAddress_PrefixTooLarge() {
+ getPrefixMaskAsInet4Address(33);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetPrefixMaskAsAddress_NegativePrefix() {
+ getPrefixMaskAsInet4Address(-1);
+ }
+
+ @Test
+ public void testGetBroadcastAddress() {
+ assertEquals("192.168.15.255",
+ getBroadcastAddress(IPv4Address("192.168.0.123"), 20).getHostAddress());
+ assertEquals("192.255.255.255",
+ getBroadcastAddress(IPv4Address("192.168.0.123"), 8).getHostAddress());
+ assertEquals("192.168.0.123",
+ getBroadcastAddress(IPv4Address("192.168.0.123"), 32).getHostAddress());
+ assertEquals("255.255.255.255",
+ getBroadcastAddress(IPv4Address("192.168.0.123"), 0).getHostAddress());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetBroadcastAddress_PrefixTooLarge() {
+ getBroadcastAddress(IPv4Address("192.168.0.123"), 33);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetBroadcastAddress_NegativePrefix() {
+ getBroadcastAddress(IPv4Address("192.168.0.123"), -1);
+ }
}
diff --git a/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java b/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
new file mode 100644
index 000000000000..edadd6ead667
--- /dev/null
+++ b/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
@@ -0,0 +1,519 @@
+/*
+ * 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 android.net.dhcp;
+
+import static android.net.dhcp.DhcpLease.HOSTNAME_NONE;
+import static android.net.dhcp.DhcpLeaseRepository.CLIENTID_UNSPEC;
+import static android.net.dhcp.DhcpLeaseRepository.INETADDR_UNSPEC;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.when;
+
+import static java.lang.String.format;
+import static java.net.InetAddress.parseNumericAddress;
+
+import android.annotation.NonNull;
+import android.net.IpPrefix;
+import android.net.MacAddress;
+import android.net.dhcp.DhcpLeaseRepository.Clock;
+import android.net.util.SharedLog;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.net.Inet4Address;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DhcpLeaseRepositoryTest {
+ private static final Inet4Address INET4_ANY = (Inet4Address) Inet4Address.ANY;
+ private static final Inet4Address TEST_DEF_ROUTER = parseAddr4("192.168.42.247");
+ private static final Inet4Address TEST_SERVER_ADDR = parseAddr4("192.168.42.241");
+ private static final Inet4Address TEST_RESERVED_ADDR = parseAddr4("192.168.42.243");
+ private static final MacAddress TEST_MAC_1 = MacAddress.fromBytes(
+ new byte[] { 5, 4, 3, 2, 1, 0 });
+ private static final MacAddress TEST_MAC_2 = MacAddress.fromBytes(
+ new byte[] { 0, 1, 2, 3, 4, 5 });
+ private static final MacAddress TEST_MAC_3 = MacAddress.fromBytes(
+ new byte[] { 0, 1, 2, 3, 4, 6 });
+ private static final Inet4Address TEST_INETADDR_1 = parseAddr4("192.168.42.248");
+ private static final Inet4Address TEST_INETADDR_2 = parseAddr4("192.168.42.249");
+ private static final String TEST_HOSTNAME_1 = "hostname1";
+ private static final String TEST_HOSTNAME_2 = "hostname2";
+ private static final IpPrefix TEST_IP_PREFIX = new IpPrefix(TEST_SERVER_ADDR, 22);
+ private static final long TEST_TIME = 100L;
+ private static final int TEST_LEASE_TIME_MS = 3_600_000;
+ private static final Set<Inet4Address> TEST_EXCL_SET =
+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+ TEST_SERVER_ADDR, TEST_DEF_ROUTER, TEST_RESERVED_ADDR)));
+
+ @NonNull
+ private SharedLog mLog;
+ @NonNull @Mock
+ private Clock mClock;
+ @NonNull
+ private DhcpLeaseRepository mRepo;
+
+ private static Inet4Address parseAddr4(String inet4Addr) {
+ return (Inet4Address) parseNumericAddress(inet4Addr);
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mLog = new SharedLog("DhcpLeaseRepositoryTest");
+ when(mClock.elapsedRealtime()).thenReturn(TEST_TIME);
+ mRepo = new DhcpLeaseRepository(
+ TEST_IP_PREFIX, TEST_EXCL_SET, TEST_LEASE_TIME_MS, mLog, mClock);
+ }
+
+ /**
+ * Request a number of addresses through offer/request. Useful to test address exhaustion.
+ * @param nAddr Number of addresses to request.
+ */
+ private void requestAddresses(byte nAddr) throws Exception {
+ final HashSet<Inet4Address> addrs = new HashSet<>();
+ byte[] hwAddrBytes = new byte[] { 8, 4, 3, 2, 1, 0 };
+ for (byte i = 0; i < nAddr; i++) {
+ hwAddrBytes[5] = i;
+ MacAddress newMac = MacAddress.fromBytes(hwAddrBytes);
+ final String hostname = "host_" + i;
+ final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, newMac,
+ INETADDR_UNSPEC /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, hostname);
+
+ assertNotNull(lease);
+ assertEquals(newMac, lease.getHwAddr());
+ assertEquals(hostname, lease.getHostname());
+ assertTrue(format("Duplicate address allocated: %s in %s", lease.getNetAddr(), addrs),
+ addrs.add(lease.getNetAddr()));
+
+ mRepo.requestLease(null, newMac, null, lease.getNetAddr(), true, hostname);
+ }
+ }
+
+ @Test
+ public void testAddressExhaustion() throws Exception {
+ // Use a /28 to quickly run out of addresses
+ mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
+
+ // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
+ requestAddresses((byte)11);
+
+ try {
+ mRepo.getOffer(null, TEST_MAC_2,
+ null /* relayAddr */, null /* reqAddr */, HOSTNAME_NONE);
+ fail("Should be out of addresses");
+ } catch (DhcpLeaseRepository.OutOfAddressesException e) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void testUpdateParams_LeaseCleanup() throws Exception {
+ // Inside /28:
+ final Inet4Address reqAddrIn28 = parseAddr4("192.168.42.242");
+ final Inet4Address declinedAddrIn28 = parseAddr4("192.168.42.245");
+
+ // Inside /28, but not available there (first address of the range)
+ final Inet4Address declinedFirstAddrIn28 = parseAddr4("192.168.42.240");
+
+ final DhcpLease reqAddrIn28Lease = mRepo.requestLease(
+ CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY, reqAddrIn28, false, HOSTNAME_NONE);
+ mRepo.markLeaseDeclined(declinedAddrIn28);
+ mRepo.markLeaseDeclined(declinedFirstAddrIn28);
+
+ // Inside /22, but outside /28:
+ final Inet4Address reqAddrIn22 = parseAddr4("192.168.42.3");
+ final Inet4Address declinedAddrIn22 = parseAddr4("192.168.42.4");
+
+ final DhcpLease reqAddrIn22Lease = mRepo.requestLease(
+ CLIENTID_UNSPEC, TEST_MAC_3, INET4_ANY, reqAddrIn22, false, HOSTNAME_NONE);
+ mRepo.markLeaseDeclined(declinedAddrIn22);
+
+ // Address that will be reserved in the updateParams call below
+ final Inet4Address reservedAddr = parseAddr4("192.168.42.244");
+ final DhcpLease reservedAddrLease = mRepo.requestLease(
+ CLIENTID_UNSPEC, TEST_MAC_2, INET4_ANY, reservedAddr, false, HOSTNAME_NONE);
+
+ // Update from /22 to /28 and add another reserved address
+ Set<Inet4Address> newReserved = new HashSet<>(TEST_EXCL_SET);
+ newReserved.add(reservedAddr);
+ mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), newReserved, TEST_LEASE_TIME_MS);
+
+ assertHasLease(reqAddrIn28Lease);
+ assertDeclined(declinedAddrIn28);
+
+ assertNotDeclined(declinedFirstAddrIn28);
+
+ assertNoLease(reqAddrIn22Lease);
+ assertNotDeclined(declinedAddrIn22);
+
+ assertNoLease(reservedAddrLease);
+ }
+
+ @Test
+ public void testGetOffer_StableAddress() throws Exception {
+ for (final MacAddress macAddr : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
+ final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
+ INETADDR_UNSPEC /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+
+ // Same lease is offered twice
+ final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
+ INETADDR_UNSPEC /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ assertEquals(lease, newLease);
+ }
+ }
+
+ @Test
+ public void testUpdateParams_UsesNewPrefix() throws Exception {
+ final IpPrefix newPrefix = new IpPrefix(parseAddr4("192.168.123.0"), 24);
+ mRepo.updateParams(newPrefix, TEST_EXCL_SET, TEST_LEASE_TIME_MS);
+
+ DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
+ INETADDR_UNSPEC, INETADDR_UNSPEC, HOSTNAME_NONE);
+ assertTrue(newPrefix.contains(lease.getNetAddr()));
+ }
+
+ @Test
+ public void testGetOffer_ExistingLease() throws Exception {
+ mRepo.requestLease(
+ CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY, TEST_INETADDR_1, false, TEST_HOSTNAME_1);
+
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
+ INETADDR_UNSPEC, INETADDR_UNSPEC, HOSTNAME_NONE);
+ assertEquals(TEST_INETADDR_1, offer.getNetAddr());
+ assertEquals(TEST_HOSTNAME_1, offer.getHostname());
+ }
+
+ @Test
+ public void testGetOffer_ClientIdHasExistingLease() throws Exception {
+ final byte[] clientId = new byte[] { 1, 2 };
+ mRepo.requestLease(clientId, TEST_MAC_1, INET4_ANY, TEST_INETADDR_1, false,
+ TEST_HOSTNAME_1);
+
+ // Different MAC, but same clientId
+ DhcpLease offer = mRepo.getOffer(clientId, TEST_MAC_2,
+ INETADDR_UNSPEC, INETADDR_UNSPEC, HOSTNAME_NONE);
+ assertEquals(TEST_INETADDR_1, offer.getNetAddr());
+ assertEquals(TEST_HOSTNAME_1, offer.getHostname());
+ }
+
+ @Test
+ public void testGetOffer_DifferentClientId() throws Exception {
+ final byte[] clientId1 = new byte[] { 1, 2 };
+ final byte[] clientId2 = new byte[] { 3, 4 };
+ mRepo.requestLease(clientId1, TEST_MAC_1, INET4_ANY, TEST_INETADDR_1, false,
+ TEST_HOSTNAME_1);
+
+ // Same MAC, different client ID
+ DhcpLease offer = mRepo.getOffer(clientId2, TEST_MAC_1,
+ INETADDR_UNSPEC, INETADDR_UNSPEC, HOSTNAME_NONE);
+ // Obtains a different address
+ assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
+ assertEquals(HOSTNAME_NONE, offer.getHostname());
+ assertEquals(TEST_MAC_1, offer.getHwAddr());
+ }
+
+ @Test
+ public void testGetOffer_RequestedAddress() throws Exception {
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ TEST_INETADDR_1, TEST_HOSTNAME_1);
+ assertEquals(TEST_INETADDR_1, offer.getNetAddr());
+ assertEquals(TEST_HOSTNAME_1, offer.getHostname());
+ }
+
+ @Test
+ public void testGetOffer_RequestedAddressInUse() throws Exception {
+ mRepo.requestLease(
+ CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY, TEST_INETADDR_1, false, HOSTNAME_NONE);
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2, INET4_ANY,
+ TEST_INETADDR_1, HOSTNAME_NONE);
+ assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
+ }
+
+ @Test
+ public void testGetOffer_RequestedAddressReserved() throws Exception {
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ TEST_RESERVED_ADDR, HOSTNAME_NONE);
+ assertNotEquals(TEST_RESERVED_ADDR, offer.getNetAddr());
+ }
+
+ @Test
+ public void testGetOffer_RequestedAddressInvalid() throws Exception {
+ final Inet4Address invalidAddr = parseAddr4("192.168.42.0");
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ invalidAddr, HOSTNAME_NONE);
+ assertNotEquals(invalidAddr, offer.getNetAddr());
+ }
+
+ @Test
+ public void testGetOffer_RequestedAddressOutsideSubnet() throws Exception {
+ final Inet4Address invalidAddr = parseAddr4("192.168.254.2");
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ invalidAddr, HOSTNAME_NONE);
+ assertNotEquals(invalidAddr, offer.getNetAddr());
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testGetOffer_RelayInInvalidSubnet() throws Exception {
+ mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
+ parseAddr4("192.168.254.2") /* relayAddr */, INETADDR_UNSPEC, HOSTNAME_NONE);
+ }
+
+ @Test
+ public void testRequestLease_SelectingTwice() throws Exception {
+ DhcpLease lease1 = mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ TEST_INETADDR_1, true /* sidSet */, TEST_HOSTNAME_1);
+
+ // Second request from same client for a different address
+ DhcpLease lease2 = mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ TEST_INETADDR_2, true /* sidSet */, TEST_HOSTNAME_2);
+
+ assertEquals(TEST_INETADDR_1, lease1.getNetAddr());
+ assertEquals(TEST_HOSTNAME_1, lease1.getHostname());
+
+ assertEquals(TEST_INETADDR_2, lease2.getNetAddr());
+ assertEquals(TEST_HOSTNAME_2, lease2.getHostname());
+
+ // First address freed when client requested a different one: another client can request it
+ DhcpLease lease3 = mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_2, INET4_ANY,
+ TEST_INETADDR_1, true /* sidSet */, HOSTNAME_NONE);
+ assertEquals(TEST_INETADDR_1, lease3.getNetAddr());
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_SelectingInvalid() throws Exception {
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ parseAddr4("192.168.254.5"), true /* sidSet */, HOSTNAME_NONE);
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_SelectingInUse() throws Exception {
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ TEST_INETADDR_1, true /* sidSet */, HOSTNAME_NONE);
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_2, INET4_ANY,
+ TEST_INETADDR_1, true /* sidSet */, HOSTNAME_NONE);
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_SelectingReserved() throws Exception {
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ TEST_RESERVED_ADDR, true /* sidSet */, HOSTNAME_NONE);
+ }
+
+ @Test
+ public void testRequestLease_InitReboot() throws Exception {
+ // Request address once
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ TEST_INETADDR_1, true /* sidSet */, HOSTNAME_NONE);
+
+ final long newTime = TEST_TIME + 100;
+ when(mClock.elapsedRealtime()).thenReturn(newTime);
+
+ // init-reboot (sidSet == false): verify configuration
+ DhcpLease lease = mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ TEST_INETADDR_1, false, HOSTNAME_NONE);
+ assertEquals(TEST_INETADDR_1, lease.getNetAddr());
+ assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_InitRebootWrongAddr() throws Exception {
+ // Request address once
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ TEST_INETADDR_1, true /* sidSet */, HOSTNAME_NONE);
+ // init-reboot with different requested address
+ mRepo.requestLease(
+ CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY, TEST_INETADDR_2, false, HOSTNAME_NONE);
+ }
+
+ @Test
+ public void testRequestLease_InitRebootUnknownAddr() throws Exception {
+ // init-reboot with unknown requested address
+ DhcpLease lease = mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ TEST_INETADDR_2, false, HOSTNAME_NONE);
+ // RFC2131 says we should not reply to accommodate other servers, but since we are
+ // authoritative we allow creating the lease to avoid issues with lost lease DB (same as
+ // dnsmasq behavior)
+ assertEquals(TEST_INETADDR_2, lease.getNetAddr());
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_InitRebootWrongSubnet() throws Exception {
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ parseAddr4("192.168.254.2"), false /* sidSet */, HOSTNAME_NONE);
+ }
+
+ @Test
+ public void testRequestLease_Renewing() throws Exception {
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1,
+ INET4_ANY /* clientAddr */, TEST_INETADDR_1 /* reqAddr */, true, HOSTNAME_NONE);
+
+ final long newTime = TEST_TIME + 100;
+ when(mClock.elapsedRealtime()).thenReturn(newTime);
+
+ // Renewing: clientAddr filled in, no reqAddr
+ DhcpLease lease = mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1,
+ TEST_INETADDR_1 /* clientAddr */, INETADDR_UNSPEC /* reqAddr */, false,
+ HOSTNAME_NONE);
+
+ assertEquals(TEST_INETADDR_1, lease.getNetAddr());
+ assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
+ }
+
+ @Test
+ public void testRequestLease_RenewingUnknownAddr() throws Exception {
+ final long newTime = TEST_TIME + 100;
+ when(mClock.elapsedRealtime()).thenReturn(newTime);
+ DhcpLease lease = mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1,
+ TEST_INETADDR_1 /* clientAddr */, INETADDR_UNSPEC /* reqAddr */, false,
+ HOSTNAME_NONE);
+ // Allows renewing an unknown address if available
+ assertEquals(TEST_INETADDR_1, lease.getNetAddr());
+ assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_RenewingAddrInUse() throws Exception {
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_2,
+ INET4_ANY /* clientAddr */, TEST_INETADDR_1 /* reqAddr */, true, HOSTNAME_NONE);
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1,
+ TEST_INETADDR_1 /* clientAddr */, INETADDR_UNSPEC /* reqAddr */, false,
+ HOSTNAME_NONE);
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_RenewingInvalidAddr() throws Exception {
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, parseAddr4("192.168.254.2") /* clientAddr */,
+ INETADDR_UNSPEC /* reqAddr */, false, HOSTNAME_NONE);
+ }
+
+ @Test
+ public void testReleaseLease() throws Exception {
+ DhcpLease lease1 = mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY,
+ TEST_INETADDR_1, true /* sidSet */, HOSTNAME_NONE);
+
+ assertHasLease(lease1);
+ assertTrue(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1));
+ assertNoLease(lease1);
+
+ DhcpLease lease2 = mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_2, INET4_ANY,
+ TEST_INETADDR_1, true /* sidSet */, HOSTNAME_NONE);
+
+ assertEquals(TEST_INETADDR_1, lease2.getNetAddr());
+ }
+
+ @Test
+ public void testReleaseLease_UnknownLease() {
+ assertFalse(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1));
+ }
+
+ @Test
+ public void testReleaseLease_StableOffer() throws Exception {
+ for (MacAddress mac : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
+ final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
+ INETADDR_UNSPEC /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ mRepo.requestLease(
+ CLIENTID_UNSPEC, mac, INET4_ANY, lease.getNetAddr(), true,
+ HOSTNAME_NONE);
+ mRepo.releaseLease(CLIENTID_UNSPEC, mac, lease.getNetAddr());
+
+ // Same lease is offered after it was released
+ final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
+ INETADDR_UNSPEC /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ assertEquals(lease.getNetAddr(), newLease.getNetAddr());
+ }
+ }
+
+ @Test
+ public void testMarkLeaseDeclined() throws Exception {
+ final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
+ INETADDR_UNSPEC /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+
+ mRepo.markLeaseDeclined(lease.getNetAddr());
+
+ // Same lease is not offered again
+ final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
+ INETADDR_UNSPEC /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ assertNotEquals(lease.getNetAddr(), newLease.getNetAddr());
+ }
+
+ @Test
+ public void testMarkLeaseDeclined_UsedIfOutOfAddresses() throws Exception {
+ // Use a /28 to quickly run out of addresses
+ mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
+
+ mRepo.markLeaseDeclined(TEST_INETADDR_1);
+ mRepo.markLeaseDeclined(TEST_INETADDR_2);
+
+ // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
+ requestAddresses((byte)9);
+
+ // Last 2 addresses: addresses marked declined should be used
+ final DhcpLease firstLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
+ INETADDR_UNSPEC /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_1);
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY, firstLease.getNetAddr(), true,
+ HOSTNAME_NONE);
+
+ final DhcpLease secondLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2,
+ INETADDR_UNSPEC /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_2);
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_2, INET4_ANY, secondLease.getNetAddr(), true,
+ HOSTNAME_NONE);
+
+ // Now out of addresses
+ try {
+ mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_3, INETADDR_UNSPEC /* relayAddr */,
+ INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ fail("Repository should be out of addresses and throw");
+ } catch (DhcpLeaseRepository.OutOfAddressesException e) { /* expected */ }
+
+ assertEquals(TEST_INETADDR_1, firstLease.getNetAddr());
+ assertEquals(TEST_HOSTNAME_1, firstLease.getHostname());
+ assertEquals(TEST_INETADDR_2, secondLease.getNetAddr());
+ assertEquals(TEST_HOSTNAME_2, secondLease.getHostname());
+ }
+
+ private void assertNoLease(DhcpLease lease) {
+ assertFalse("Leases contain " + lease, mRepo.getCommittedLeases().contains(lease));
+ }
+
+ private void assertHasLease(DhcpLease lease) {
+ assertTrue("Leases do not contain " + lease, mRepo.getCommittedLeases().contains(lease));
+ }
+
+ private void assertNotDeclined(Inet4Address addr) {
+ assertFalse("Address is declined: " + addr, mRepo.getDeclinedAddresses().contains(addr));
+ }
+
+ private void assertDeclined(Inet4Address addr) {
+ assertTrue("Address is not declined: " + addr, mRepo.getDeclinedAddresses().contains(addr));
+ }
+}
diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java b/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
new file mode 100644
index 000000000000..dfa09a9f205c
--- /dev/null
+++ b/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
@@ -0,0 +1,177 @@
+/*
+ * 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 android.net.dhcp;
+
+import static android.net.dhcp.DhcpServingParams.MTU_UNSET;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static java.net.InetAddress.parseNumericAddress;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.LinkAddress;
+import android.net.dhcp.DhcpServingParams.InvalidParameterException;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DhcpServingParamsTest {
+ @NonNull
+ private DhcpServingParams.Builder mBuilder;
+
+ private static final Set<Inet4Address> TEST_DEFAULT_ROUTERS = new HashSet<>(
+ Arrays.asList(parseAddr("192.168.0.123"), parseAddr("192.168.0.124")));
+ private static final long TEST_LEASE_TIME_SECS = 3600L;
+ private static final Set<Inet4Address> TEST_DNS_SERVERS = new HashSet<>(
+ Arrays.asList(parseAddr("192.168.0.126"), parseAddr("192.168.0.127")));
+ private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
+ private static final LinkAddress TEST_LINKADDR = new LinkAddress(TEST_SERVER_ADDR, 20);
+ private static final int TEST_MTU = 1500;
+ private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>(
+ Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201")));
+
+ @Before
+ public void setUp() {
+ mBuilder = new DhcpServingParams.Builder()
+ .setDefaultRouters(TEST_DEFAULT_ROUTERS)
+ .setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS)
+ .setDnsServers(TEST_DNS_SERVERS)
+ .setServerAddr(TEST_LINKADDR)
+ .setLinkMtu(TEST_MTU)
+ .setExcludedAddrs(TEST_EXCLUDED_ADDRS);
+ }
+
+ @Test
+ public void testBuild_Immutable() throws InvalidParameterException {
+ final Set<Inet4Address> routers = new HashSet<>(TEST_DEFAULT_ROUTERS);
+ final Set<Inet4Address> dnsServers = new HashSet<>(TEST_DNS_SERVERS);
+ final Set<Inet4Address> excludedAddrs = new HashSet<>(TEST_EXCLUDED_ADDRS);
+
+ final DhcpServingParams params = mBuilder
+ .setDefaultRouters(routers)
+ .setDnsServers(dnsServers)
+ .setExcludedAddrs(excludedAddrs)
+ .build();
+
+ // Modifications to source objects should not affect builder or final parameters
+ final Inet4Address addedAddr = parseAddr("192.168.0.223");
+ routers.add(addedAddr);
+ dnsServers.add(addedAddr);
+ excludedAddrs.add(addedAddr);
+
+ assertEquals(TEST_DEFAULT_ROUTERS, params.defaultRouters);
+ assertEquals(TEST_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
+ assertEquals(TEST_DNS_SERVERS, params.dnsServers);
+ assertEquals(TEST_LINKADDR, params.serverAddr);
+ assertEquals(TEST_MTU, params.linkMtu);
+
+ assertContains(params.excludedAddrs, TEST_EXCLUDED_ADDRS);
+ assertContains(params.excludedAddrs, TEST_DEFAULT_ROUTERS);
+ assertContains(params.excludedAddrs, TEST_DNS_SERVERS);
+ assertContains(params.excludedAddrs, TEST_SERVER_ADDR);
+
+ assertFalse("excludedAddrs should not contain " + addedAddr,
+ params.excludedAddrs.contains(addedAddr));
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testBuild_NegativeLeaseTime() throws InvalidParameterException {
+ mBuilder.setDhcpLeaseTimeSecs(-1).build();
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testBuild_LeaseTimeTooLarge() throws InvalidParameterException {
+ // Set lease time larger than max value for uint32
+ mBuilder.setDhcpLeaseTimeSecs(1L << 32).build();
+ }
+
+ @Test
+ public void testBuild_InfiniteLeaseTime() throws InvalidParameterException {
+ final long infiniteLeaseTime = 0xffffffffL;
+ final DhcpServingParams params = mBuilder
+ .setDhcpLeaseTimeSecs(infiniteLeaseTime).build();
+ assertEquals(infiniteLeaseTime, params.dhcpLeaseTimeSecs);
+ assertTrue(params.dhcpLeaseTimeSecs > 0L);
+ }
+
+ @Test
+ public void testBuild_UnsetMtu() throws InvalidParameterException {
+ final DhcpServingParams params = mBuilder.setLinkMtu(MTU_UNSET).build();
+ assertEquals(MTU_UNSET, params.linkMtu);
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testBuild_MtuTooSmall() throws InvalidParameterException {
+ mBuilder.setLinkMtu(20).build();
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testBuild_MtuTooLarge() throws InvalidParameterException {
+ mBuilder.setLinkMtu(65_536).build();
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testBuild_IPv6Addr() throws InvalidParameterException {
+ mBuilder.setServerAddr(new LinkAddress(parseNumericAddress("fe80::1111"), 120)).build();
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testBuild_PrefixTooLarge() throws InvalidParameterException {
+ mBuilder.setServerAddr(new LinkAddress(TEST_SERVER_ADDR, 15)).build();
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testBuild_PrefixTooSmall() throws InvalidParameterException {
+ mBuilder.setDefaultRouters(Collections.singleton(parseAddr("192.168.0.254")))
+ .setServerAddr(new LinkAddress(TEST_SERVER_ADDR, 31))
+ .build();
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testBuild_RouterNotInPrefix() throws InvalidParameterException {
+ mBuilder.setDefaultRouters(Collections.singleton(parseAddr("192.168.254.254"))).build();
+ }
+
+ private static <T> void assertContains(@NonNull Set<T> set, @NonNull Set<T> subset) {
+ for (final T elem : subset) {
+ assertContains(set, elem);
+ }
+ }
+
+ private static <T> void assertContains(@NonNull Set<T> set, @Nullable T elem) {
+ assertTrue("Set does not contain " + elem, set.contains(elem));
+ }
+
+ @NonNull
+ private static Inet4Address parseAddr(@NonNull String inet4Addr) {
+ return (Inet4Address) parseNumericAddress(inet4Addr);
+ }
+}
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 55a32c8b512c..c5c78d9d3827 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -357,6 +357,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
manifest_action["uses-permission"];
manifest_action["uses-permission-sdk-23"];
manifest_action["permission"];
+ manifest_action["permission"]["meta-data"] = meta_data_action;
manifest_action["permission-tree"];
manifest_action["permission-group"];
manifest_action["uses-configuration"];
@@ -366,6 +367,8 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
manifest_action["compatible-screens"];
manifest_action["compatible-screens"]["screen"];
manifest_action["supports-gl-texture"];
+ manifest_action["restrict-update"];
+ manifest_action["package-verifier"];
manifest_action["meta-data"] = meta_data_action;
manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);
@@ -387,6 +390,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
uses_static_library_action.Action(RequiredNameIsJavaPackage);
uses_static_library_action.Action(RequiredAndroidAttribute("version"));
uses_static_library_action.Action(RequiredAndroidAttribute("certDigest"));
+ uses_static_library_action["additional-certificate"];
if (options_.debug_mode) {
application_action.Action([&](xml::Element* el) -> bool {
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
new file mode 100755
index 000000000000..29bf74c7a8b9
--- /dev/null
+++ b/tools/aosp/aosp_sha.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
+
+if git branch -vv | grep "^*" | grep "\[aosp/master" > /dev/null; then
+ # Change appears to be in AOSP
+ exit 0
+else
+ # Change appears to be non-AOSP; search for files
+ git show --name-only --pretty=format: $1 | grep $2 | while read file; do
+ echo
+ echo -e "\033[0;31mThe source of truth for '$file' is in AOSP.\033[0m"
+ echo
+ echo "If your change contains no confidential details, please upload and merge"
+ echo "this change at https://android-review.googlesource.com/."
+ echo
+ exit 77
+ done
+fi
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index e519909aa026..8038a3a4f44e 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -349,8 +349,7 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
argIndex++;
}
fprintf(out, ");\n");
- fprintf(out, " if (ret >= 0) { return retry; }\n");
-
+ fprintf(out, " if (ret >= 0) { break; }\n");
fprintf(out, " {\n");
fprintf(out, " std::lock_guard<std::mutex> lock(mLogdRetryMutex);\n");
@@ -360,6 +359,9 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
fprintf(out, " }\n");
fprintf(out, " std::this_thread::sleep_for(std::chrono::milliseconds(10));\n");
fprintf(out, " }\n");
+ fprintf(out, " if (ret < 0) {\n");
+ fprintf(out, " note_log_drop();\n");
+ fprintf(out, " }\n");
fprintf(out, " return ret;\n");
fprintf(out, "}\n");
fprintf(out, "\n");
@@ -439,7 +441,7 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
argIndex++;
}
fprintf(out, ");\n");
- fprintf(out, " if (ret >= 0) { return retry; }\n");
+ fprintf(out, " if (ret >= 0) { break; }\n");
fprintf(out, " {\n");
fprintf(out, " std::lock_guard<std::mutex> lock(mLogdRetryMutex);\n");
@@ -450,7 +452,10 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
fprintf(out, " std::this_thread::sleep_for(std::chrono::milliseconds(10));\n");
fprintf(out, " }\n");
- fprintf(out, " return ret;\n");
+ fprintf(out, " if (ret < 0) {\n");
+ fprintf(out, " note_log_drop();\n");
+ fprintf(out, " }\n");
+ fprintf(out, " return ret;\n\n");
fprintf(out, "}\n");
fprintf(out, "\n");