Merge "[java9] Convert hiddenapi-list target to Metalava"
diff --git a/Android.mk b/Android.mk
index 29454e4..84b708e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -325,6 +325,7 @@
include $(CLEAR_VARS)
# File names of final API lists
+LOCAL_WHITELIST := $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST)
LOCAL_LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
LOCAL_DARK_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
LOCAL_BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
@@ -374,6 +375,13 @@
# or have alternative rules for building them. Other rules in the build system
# should depend on the files in the build folder.
+# Merge whitelist from:
+# (1) public API stubs
+# (2) whitelist entries generated by class2greylist (PRIVATE_WHITELIST_INPUTS)
+$(LOCAL_WHITELIST): $(LOCAL_SRC_PUBLIC_API)
+ sort $(LOCAL_SRC_PUBLIC_API) $(PRIVATE_WHITELIST_INPUTS) > $@
+ $(call assert-has-no-duplicates,$@)
+
# Merge light greylist from multiple files:
# (1) manual greylist LOCAL_SRC_GREYLIST
# (2) list of usages from vendor apps LOCAL_SRC_VENDOR_LIST
@@ -384,6 +392,7 @@
# Automatically adds all methods which match the signatures in
# REGEX_SERIALIZATION. These are greylisted in order to allow applications
# to write their own serializers.
+# (5) greylist entries generated by class2greylist (PRIVATE_GREYLIST_INPUTS)
$(LOCAL_LIGHT_GREYLIST): REGEX_SERIALIZATION := \
"readObject\(Ljava/io/ObjectInputStream;\)V" \
"readObjectNoData\(\)V" \
@@ -392,7 +401,7 @@
"serialPersistentFields:\[Ljava/io/ObjectStreamField;" \
"writeObject\(Ljava/io/ObjectOutputStream;\)V" \
"writeReplace\(\)Ljava/lang/Object;"
-$(LOCAL_LIGHT_GREYLIST): $(LOCAL_SRC_ALL)
+$(LOCAL_LIGHT_GREYLIST): $(LOCAL_SRC_ALL) $(LOCAL_WHITELIST)
sort $(LOCAL_SRC_GREYLIST) $(LOCAL_SRC_VENDOR_LIST) $(PRIVATE_GREYLIST_INPUTS) \
<(grep -E "\->("$(subst $(space),"|",$(REGEX_SERIALIZATION))")$$" \
$(LOCAL_SRC_PRIVATE_API)) \
@@ -400,6 +409,7 @@
> $@
$(call assert-has-no-duplicates,$@)
$(call assert-is-subset,$@,$(LOCAL_SRC_PRIVATE_API))
+ $(call assert-has-no-overlap,$@,$(LOCAL_WHITELIST))
$(call assert-has-no-overlap,$@,$(LOCAL_SRC_FORCE_BLACKLIST))
# Generate dark greylist as remaining classes and class members in the same
@@ -412,9 +422,9 @@
# name but do not contain another forward-slash in the class name, e.g.
# matching '^Lpackage/subpackage/[^/;]*;'
# (4) subtract entries shared with LOCAL_LIGHT_GREYLIST
-$(LOCAL_DARK_GREYLIST): $(LOCAL_SRC_ALL) $(LOCAL_LIGHT_GREYLIST)
- comm -13 <(sort $(LOCAL_LIGHT_GREYLIST) $(LOCAL_SRC_FORCE_BLACKLIST)) \
- <(cat $(LOCAL_SRC_PUBLIC_API) $(LOCAL_LIGHT_GREYLIST) | \
+$(LOCAL_DARK_GREYLIST): $(LOCAL_SRC_ALL) $(LOCAL_WHITELIST) $(LOCAL_LIGHT_GREYLIST)
+ comm -13 <(sort $(LOCAL_WHITELIST) $(LOCAL_LIGHT_GREYLIST) $(LOCAL_SRC_FORCE_BLACKLIST)) \
+ <(cat $(LOCAL_WHITELIST) $(LOCAL_LIGHT_GREYLIST) | \
sed 's/\->.*//' | sed 's/\(.*\/\).*/\1/' | sort | uniq | \
while read PKG_NAME; do \
grep -E "^$${PKG_NAME}[^/;]*;" $(LOCAL_SRC_PRIVATE_API); \
@@ -422,16 +432,18 @@
> $@
$(call assert-is-subset,$@,$(LOCAL_SRC_PRIVATE_API))
$(call assert-has-no-duplicates,$@)
+ $(call assert-has-no-overlap,$@,$(LOCAL_WHITELIST))
$(call assert-has-no-overlap,$@,$(LOCAL_LIGHT_GREYLIST))
$(call assert-has-no-overlap,$@,$(LOCAL_SRC_FORCE_BLACKLIST))
# Generate blacklist as private API minus (light greylist plus dark greylist).
-$(LOCAL_BLACKLIST): $(LOCAL_SRC_ALL) $(LOCAL_LIGHT_GREYLIST) $(LOCAL_DARK_GREYLIST)
- comm -13 <(sort $(LOCAL_LIGHT_GREYLIST) $(LOCAL_DARK_GREYLIST)) \
+$(LOCAL_BLACKLIST): $(LOCAL_SRC_ALL) $(LOCAL_WHITELIST) $(LOCAL_LIGHT_GREYLIST) $(LOCAL_DARK_GREYLIST)
+ comm -13 <(sort $(LOCAL_WHITELIST) $(LOCAL_LIGHT_GREYLIST) $(LOCAL_DARK_GREYLIST)) \
<(sort $(LOCAL_SRC_PRIVATE_API)) \
> $@
$(call assert-is-subset,$@,$(LOCAL_SRC_PRIVATE_API))
$(call assert-has-no-duplicates,$@)
+ $(call assert-has-no-overlap,$@,$(LOCAL_WHITELIST))
$(call assert-has-no-overlap,$@,$(LOCAL_LIGHT_GREYLIST))
$(call assert-has-no-overlap,$@,$(LOCAL_DARK_GREYLIST))
$(call assert-is-subset,$(LOCAL_SRC_FORCE_BLACKLIST),$@)
diff --git a/api/current.txt b/api/current.txt
index 1cb9186..3c7dcc8b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -32229,6 +32229,7 @@
field public static final int O = 26; // 0x1a
field public static final int O_MR1 = 27; // 0x1b
field public static final int P = 28; // 0x1c
+ field public static final int Q = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 34010c5..01bc351 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5789,6 +5789,7 @@
field public static final int CODE_NETWORK_DETACH = 1513; // 0x5e9
field public static final int CODE_NETWORK_REJECT = 1504; // 0x5e0
field public static final int CODE_NETWORK_RESP_TIMEOUT = 1503; // 0x5df
+ field public static final int CODE_NO_CSFB_IN_CS_ROAM = 1516; // 0x5ec
field public static final int CODE_NO_VALID_SIM = 1501; // 0x5dd
field public static final int CODE_OEM_CAUSE_1 = 61441; // 0xf001
field public static final int CODE_OEM_CAUSE_10 = 61450; // 0xf00a
@@ -5857,6 +5858,7 @@
field public static final int CODE_SIP_SERVER_TIMEOUT = 353; // 0x161
field public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352; // 0x160
field public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336; // 0x150
+ field public static final int CODE_SIP_USER_MARKED_UNWANTED = 365; // 0x16d
field public static final int CODE_SIP_USER_REJECTED = 361; // 0x169
field public static final int CODE_SUPP_SVC_CANCELLED = 1202; // 0x4b2
field public static final int CODE_SUPP_SVC_FAILED = 1201; // 0x4b1
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index a787736..9ca0745 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -12204,11 +12204,11 @@
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/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;->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/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 bdaaba4..19ddb6c 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1,14 +1,11 @@
-Landroid/accessibilityservice/IAccessibilityServiceConnection$Stub;-><init>()V
Landroid/accessibilityservice/IAccessibilityServiceConnection$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accessibilityservice/IAccessibilityServiceConnection;
Landroid/accounts/IAccountAuthenticator$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/accounts/IAccountAuthenticator$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/accounts/IAccountAuthenticator$Stub;-><init>()V
Landroid/accounts/IAccountAuthenticator$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accounts/IAccountAuthenticator;
Landroid/accounts/IAccountAuthenticator;->addAccount(Landroid/accounts/IAccountAuthenticatorResponse;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Landroid/os/Bundle;)V
-Landroid/accounts/IAccountAuthenticator;->addAccountFromCredentials(Landroid/accounts/IAccountAuthenticatorResponse;Landroid/accounts/Account;Landroid/os/Bundle;)V
Landroid/accounts/IAccountAuthenticator;->confirmCredentials(Landroid/accounts/IAccountAuthenticatorResponse;Landroid/accounts/Account;Landroid/os/Bundle;)V
Landroid/accounts/IAccountAuthenticator;->editProperties(Landroid/accounts/IAccountAuthenticatorResponse;Ljava/lang/String;)V
-Landroid/accounts/IAccountAuthenticator;->getAccountCredentialsForCloning(Landroid/accounts/IAccountAuthenticatorResponse;Landroid/accounts/Account;)V
Landroid/accounts/IAccountAuthenticator;->getAccountRemovalAllowed(Landroid/accounts/IAccountAuthenticatorResponse;Landroid/accounts/Account;)V
Landroid/accounts/IAccountAuthenticator;->getAuthToken(Landroid/accounts/IAccountAuthenticatorResponse;Landroid/accounts/Account;Ljava/lang/String;Landroid/os/Bundle;)V
Landroid/accounts/IAccountAuthenticator;->getAuthTokenLabel(Landroid/accounts/IAccountAuthenticatorResponse;Ljava/lang/String;)V
@@ -51,9 +48,6 @@
Landroid/app/backup/IFullBackupRestoreObserver$Stub;-><init>()V
Landroid/app/backup/IRestoreObserver$Stub;-><init>()V
Landroid/app/DownloadManager;->restartDownload([J)V
-Landroid/app/IActivityController;->activityResuming(Ljava/lang/String;)Z
-Landroid/app/IActivityController;->activityStarting(Landroid/content/Intent;Ljava/lang/String;)Z
-Landroid/app/IActivityController;->appNotResponding(Ljava/lang/String;ILjava/lang/String;)I
Landroid/app/IActivityManager$Stub$Proxy;->getConfiguration()Landroid/content/res/Configuration;
Landroid/app/IActivityManager$Stub$Proxy;->getLaunchedFromUid(Landroid/os/IBinder;)I
Landroid/app/IActivityManager$Stub$Proxy;->getProcessLimit()I
@@ -66,7 +60,6 @@
Landroid/app/IActivityManager;->bindService(Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/content/Intent;Ljava/lang/String;Landroid/app/IServiceConnection;ILjava/lang/String;I)I
Landroid/app/IActivityManager;->broadcastIntent(Landroid/app/IApplicationThread;Landroid/content/Intent;Ljava/lang/String;Landroid/content/IIntentReceiver;ILjava/lang/String;Landroid/os/Bundle;[Ljava/lang/String;ILandroid/os/Bundle;ZZI)I
Landroid/app/IActivityManager;->checkPermission(Ljava/lang/String;II)I
-Landroid/app/IActivityManager;->enterSafeMode()V
Landroid/app/IActivityManager;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z
Landroid/app/IActivityManager;->finishHeavyWeightApp()V
Landroid/app/IActivityManager;->finishReceiver(Landroid/os/IBinder;ILjava/lang/String;Landroid/os/Bundle;ZI)V
@@ -92,7 +85,6 @@
Landroid/app/IActivityManager;->isTopOfTask(Landroid/os/IBinder;)Z
Landroid/app/IActivityManager;->isUserRunning(II)Z
Landroid/app/IActivityManager;->killAllBackgroundProcesses()V
-Landroid/app/IActivityManager;->killApplicationProcess(Ljava/lang/String;I)V
Landroid/app/IActivityManager;->killBackgroundProcesses(Ljava/lang/String;I)V
Landroid/app/IActivityManager;->moveActivityTaskToBack(Landroid/os/IBinder;Z)Z
Landroid/app/IActivityManager;->moveTaskToFront(IILandroid/os/Bundle;)V
@@ -135,7 +127,6 @@
Landroid/app/IActivityManager;->stopUser(IZLandroid/app/IStopUserCallback;)I
Landroid/app/IActivityManager;->suppressResizeConfigChanges(Z)V
Landroid/app/IActivityManager;->switchUser(I)Z
-Landroid/app/IActivityManager;->unbindBackupAgent(Landroid/content/pm/ApplicationInfo;)V
Landroid/app/IActivityManager;->unbindService(Landroid/app/IServiceConnection;)Z
Landroid/app/IActivityManager;->unhandledBack()V
Landroid/app/IActivityManager;->unlockUser(I[B[BLandroid/os/IProgressListener;)Z
@@ -145,32 +136,23 @@
Landroid/app/IActivityManager;->updateConfiguration(Landroid/content/res/Configuration;)Z
Landroid/app/IActivityManager;->updatePersistentConfiguration(Landroid/content/res/Configuration;)V
Landroid/app/IAlarmManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Landroid/app/IAlarmManager$Stub;-><init>()V
Landroid/app/IAlarmManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IAlarmManager;
Landroid/app/IAlarmManager$Stub;->TRANSACTION_remove:I
Landroid/app/IAlarmManager$Stub;->TRANSACTION_set:I
Landroid/app/IAlarmManager;->getNextAlarmClock(I)Landroid/app/AlarmManager$AlarmClockInfo;
Landroid/app/IAlarmManager;->set(Ljava/lang/String;IJJJILandroid/app/PendingIntent;Landroid/app/IAlarmListener;Ljava/lang/String;Landroid/os/WorkSource;Landroid/app/AlarmManager$AlarmClockInfo;)V
-Landroid/app/IApplicationThread;->processInBackground()V
Landroid/app/IApplicationThread;->scheduleBindService(Landroid/os/IBinder;Landroid/content/Intent;ZI)V
Landroid/app/IApplicationThread;->scheduleCreateService(Landroid/os/IBinder;Landroid/content/pm/ServiceInfo;Landroid/content/res/CompatibilityInfo;I)V
-Landroid/app/IApplicationThread;->scheduleExit()V
-Landroid/app/IApplicationThread;->scheduleLowMemory()V
Landroid/app/IApplicationThread;->scheduleStopService(Landroid/os/IBinder;)V
-Landroid/app/IApplicationThread;->scheduleSuicide()V
Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V
Landroid/app/IApplicationThread;->scheduleUnbindService(Landroid/os/IBinder;Landroid/content/Intent;)V
-Landroid/app/IApplicationThread;->updateTimeZone()V
Landroid/app/IAppTask;->getTaskInfo()Landroid/app/ActivityManager$RecentTaskInfo;
-Landroid/app/IBackupAgent$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IBackupAgent;
Landroid/app/IInputForwarder;->forwardEvent(Landroid/view/InputEvent;)Z
Landroid/app/IInstrumentationWatcher$Stub;-><init>()V
Landroid/app/IInstrumentationWatcher$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IInstrumentationWatcher;
-Landroid/app/IInstrumentationWatcher;->instrumentationFinished(Landroid/content/ComponentName;ILandroid/os/Bundle;)V
Landroid/app/IInstrumentationWatcher;->instrumentationStatus(Landroid/content/ComponentName;ILandroid/os/Bundle;)V
Landroid/app/INotificationManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/app/INotificationManager$Stub$Proxy;->areNotificationsEnabledForPackage(Ljava/lang/String;I)Z
-Landroid/app/INotificationManager$Stub;-><init>()V
Landroid/app/INotificationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/INotificationManager;
Landroid/app/INotificationManager$Stub;->TRANSACTION_enqueueNotificationWithTag:I
Landroid/app/INotificationManager;->areNotificationsEnabledForPackage(Ljava/lang/String;I)Z
@@ -198,13 +180,11 @@
Landroid/app/IStopUserCallback$Stub;-><init>()V
Landroid/app/IStopUserCallback;->userStopped(I)V
Landroid/app/ITransientNotification$Stub;-><init>()V
-Landroid/app/ITransientNotification;->hide()V
Landroid/app/ITransientNotification;->show(Landroid/os/IBinder;)V
Landroid/app/IUiModeManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/app/IUiModeManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IUiModeManager;
Landroid/app/IUiModeManager;->disableCarMode(I)V
Landroid/app/IUserSwitchObserver$Stub;-><init>()V
-Landroid/app/IWallpaperManager$Stub;-><init>()V
Landroid/app/IWallpaperManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IWallpaperManager;
Landroid/app/IWallpaperManager;->getHeightHint()I
Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor;
@@ -213,7 +193,6 @@
Landroid/app/IWallpaperManager;->hasNamedWallpaper(Ljava/lang/String;)Z
Landroid/app/IWallpaperManager;->setWallpaperComponent(Landroid/content/ComponentName;)V
Landroid/app/IWallpaperManagerCallback$Stub;-><init>()V
-Landroid/app/IWallpaperManagerCallback;->onWallpaperChanged()V
Landroid/app/job/IJobCallback$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/app/job/IJobCallback$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/app/job/IJobCallback$Stub;-><init>()V
@@ -307,7 +286,6 @@
Landroid/content/IIntentReceiver$Stub;-><init>()V
Landroid/content/IIntentReceiver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IIntentReceiver;
Landroid/content/IIntentReceiver;->performReceive(Landroid/content/Intent;ILjava/lang/String;Landroid/os/Bundle;ZZI)V
-Landroid/content/IIntentSender$Stub;-><init>()V
Landroid/content/IIntentSender$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IIntentSender;
Landroid/content/IOnPrimaryClipChangedListener$Stub;-><init>()V
Landroid/content/IOnPrimaryClipChangedListener$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IOnPrimaryClipChangedListener;
@@ -378,7 +356,6 @@
Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackagesForUid(I)[Ljava/lang/String;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getSystemSharedLibraryNames()[Ljava/lang/String;
-Landroid/content/pm/IPackageManager$Stub;-><init>()V
Landroid/content/pm/IPackageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageManager;
Landroid/content/pm/IPackageManager$Stub;->TRANSACTION_getApplicationInfo:I
Landroid/content/pm/IPackageManager;->addPermission(Landroid/content/pm/PermissionInfo;)Z
@@ -386,12 +363,10 @@
Landroid/content/pm/IPackageManager;->canonicalToCurrentPackageNames([Ljava/lang/String;)[Ljava/lang/String;
Landroid/content/pm/IPackageManager;->checkPermission(Ljava/lang/String;Ljava/lang/String;I)I
Landroid/content/pm/IPackageManager;->checkSignatures(Ljava/lang/String;Ljava/lang/String;)I
-Landroid/content/pm/IPackageManager;->checkUidPermission(Ljava/lang/String;I)I
Landroid/content/pm/IPackageManager;->checkUidSignatures(II)I
Landroid/content/pm/IPackageManager;->clearPackagePreferredActivities(Ljava/lang/String;)V
Landroid/content/pm/IPackageManager;->currentToCanonicalPackageNames([Ljava/lang/String;)[Ljava/lang/String;
Landroid/content/pm/IPackageManager;->deleteApplicationCacheFiles(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)V
-Landroid/content/pm/IPackageManager;->enterSafeMode()V
Landroid/content/pm/IPackageManager;->getApplicationEnabledSetting(Ljava/lang/String;I)I
Landroid/content/pm/IPackageManager;->getAppOpPermissionPackages(Ljava/lang/String;)[Ljava/lang/String;
Landroid/content/pm/IPackageManager;->getBlockUninstallForUser(Ljava/lang/String;I)Z
@@ -420,7 +395,6 @@
Landroid/content/pm/IPackageManager;->grantRuntimePermission(Ljava/lang/String;Ljava/lang/String;I)V
Landroid/content/pm/IPackageManager;->hasSystemUidErrors()Z
Landroid/content/pm/IPackageManager;->isPackageAvailable(Ljava/lang/String;I)Z
-Landroid/content/pm/IPackageManager;->isProtectedBroadcast(Ljava/lang/String;)Z
Landroid/content/pm/IPackageManager;->isSafeMode()Z
Landroid/content/pm/IPackageManager;->isStorageLow()Z
Landroid/content/pm/IPackageManager;->isUidPrivileged(I)Z
@@ -436,7 +410,6 @@
Landroid/content/pm/IPackageManager;->setInstallerPackageName(Ljava/lang/String;Ljava/lang/String;)V
Landroid/content/pm/IPackageManager;->setLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;ILandroid/content/IntentFilter;ILandroid/content/ComponentName;)V
Landroid/content/pm/IPackageManager;->setPackageStoppedState(Ljava/lang/String;ZI)V
-Landroid/content/pm/IPackageManager;->systemReady()V
Landroid/content/pm/IPackageMoveObserver$Stub;-><init>()V
Landroid/content/pm/IPackageMoveObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageMoveObserver;
Landroid/content/pm/IPackageStatsObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -567,7 +540,6 @@
Landroid/location/ILocationListener;->onProviderEnabled(Ljava/lang/String;)V
Landroid/location/ILocationListener;->onStatusChanged(Ljava/lang/String;ILandroid/os/Bundle;)V
Landroid/location/ILocationManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Landroid/location/ILocationManager$Stub;-><init>()V
Landroid/location/ILocationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationManager;
Landroid/location/ILocationManager$Stub;->TRANSACTION_getAllProviders:I
Landroid/location/ILocationManager;->getAllProviders()Ljava/util/List;
@@ -600,7 +572,6 @@
Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableUsbRegexs()[Ljava/lang/String;
Landroid/net/IConnectivityManager$Stub$Proxy;->getTetheredIfaces()[Ljava/lang/String;
Landroid/net/IConnectivityManager$Stub$Proxy;->mRemote:Landroid/os/IBinder;
-Landroid/net/IConnectivityManager$Stub;-><init>()V
Landroid/net/IConnectivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/IConnectivityManager;
Landroid/net/IConnectivityManager;->getActiveLinkProperties()Landroid/net/LinkProperties;
Landroid/net/IConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
@@ -640,7 +611,6 @@
Landroid/net/nsd/INsdManager;->getMessenger()Landroid/os/Messenger;
Landroid/net/SntpClient;-><init>()V
Landroid/net/wifi/IWifiManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Landroid/net/wifi/IWifiManager$Stub;-><init>()V
Landroid/net/wifi/IWifiManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/wifi/IWifiManager;
Landroid/net/wifi/IWifiManager$Stub;->TRANSACTION_getScanResults:I
Landroid/net/wifi/IWifiManager;->getCurrentNetwork()Landroid/net/Network;
@@ -884,11 +854,9 @@
Landroid/os/INetworkManagementService;->tetherInterface(Ljava/lang/String;)V
Landroid/os/INetworkManagementService;->untetherInterface(Ljava/lang/String;)V
Landroid/os/IPermissionController$Stub$Proxy;->checkPermission(Ljava/lang/String;II)Z
-Landroid/os/IPermissionController$Stub;-><init>()V
Landroid/os/IPermissionController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPermissionController;
Landroid/os/IPowerManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/os/IPowerManager$Stub$Proxy;->isLightDeviceIdleMode()Z
-Landroid/os/IPowerManager$Stub;-><init>()V
Landroid/os/IPowerManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPowerManager;
Landroid/os/IPowerManager$Stub;->TRANSACTION_acquireWakeLock:I
Landroid/os/IPowerManager$Stub;->TRANSACTION_goToSleep:I
@@ -1599,7 +1567,6 @@
Landroid/service/wallpaper/IWallpaperEngine;->destroy()V
Landroid/service/wallpaper/IWallpaperEngine;->dispatchPointer(Landroid/view/MotionEvent;)V
Landroid/service/wallpaper/IWallpaperEngine;->dispatchWallpaperCommand(Ljava/lang/String;IIILandroid/os/Bundle;)V
-Landroid/service/wallpaper/IWallpaperEngine;->setDesiredSize(II)V
Landroid/service/wallpaper/IWallpaperEngine;->setVisibility(Z)V
Landroid/service/wallpaper/IWallpaperService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/wallpaper/IWallpaperService;
Landroid/speech/IRecognitionListener;->onEvent(ILandroid/os/Bundle;)V
@@ -1677,28 +1644,18 @@
Landroid/view/accessibility/IAccessibilityInteractionConnectionCallback;->setFindAccessibilityNodeInfosResult(Ljava/util/List;I)V
Landroid/view/accessibility/IAccessibilityInteractionConnectionCallback;->setPerformAccessibilityActionResult(ZI)V
Landroid/view/accessibility/IAccessibilityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Landroid/view/accessibility/IAccessibilityManager$Stub;-><init>()V
Landroid/view/accessibility/IAccessibilityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/accessibility/IAccessibilityManager;
Landroid/view/accessibility/IAccessibilityManager;->getEnabledAccessibilityServiceList(II)Ljava/util/List;
Landroid/view/AccessibilityIterators$AbstractTextSegmentIterator;-><init>()V
Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/view/autofill/IAutoFillManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/autofill/IAutoFillManager;
-Landroid/view/IApplicationToken$Stub;-><init>()V
Landroid/view/IDockedStackListener$Stub;-><init>()V
Landroid/view/IGraphicsStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/view/IGraphicsStats$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IGraphicsStats;
-Landroid/view/IOnKeyguardExitResult;->onKeyguardExitResult(Z)V
Landroid/view/IRecentsAnimationController;->setAnimationTargetsBehindSystemBars(Z)V
Landroid/view/IRotationWatcher$Stub;-><init>()V
-Landroid/view/IRotationWatcher;->onRotationChanged(I)V
Landroid/view/IWindow$Stub;-><init>()V
Landroid/view/IWindow$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindow;
-Landroid/view/IWindow;->closeSystemDialogs(Ljava/lang/String;)V
-Landroid/view/IWindow;->dispatchAppVisibility(Z)V
-Landroid/view/IWindow;->dispatchGetNewSurface()V
-Landroid/view/IWindow;->dispatchWallpaperCommand(Ljava/lang/String;IIILandroid/os/Bundle;Z)V
-Landroid/view/IWindow;->dispatchWallpaperOffsets(FFFFZ)V
-Landroid/view/IWindow;->windowFocusChanged(ZZ)V
Landroid/view/IWindowManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/view/IWindowManager$Stub$Proxy;->getBaseDisplayDensity(I)I
Landroid/view/IWindowManager$Stub$Proxy;->getDockedStackSide()I
@@ -1717,7 +1674,6 @@
Landroid/view/IWindowManager;->getInitialDisplaySize(ILandroid/graphics/Point;)V
Landroid/view/IWindowManager;->getPendingAppTransition()I
Landroid/view/IWindowManager;->hasNavigationBar()Z
-Landroid/view/IWindowManager;->inputMethodClientHasFocus(Lcom/android/internal/view/IInputMethodClient;)Z
Landroid/view/IWindowManager;->isKeyguardLocked()Z
Landroid/view/IWindowManager;->isKeyguardSecure()Z
Landroid/view/IWindowManager;->isSafeModeEnabled()Z
@@ -1732,7 +1688,6 @@
Landroid/view/IWindowManager;->showStrictModeViolation(Z)V
Landroid/view/IWindowManager;->thawRotation()V
Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
-Landroid/view/IWindowSession$Stub;-><init>()V
Landroid/view/IWindowSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowSession;
Landroid/view/IWindowSession;->finishDrawing(Landroid/view/IWindow;)V
Landroid/view/IWindowSession;->getInTouchMode()Z
@@ -1803,32 +1758,15 @@
Lcom/android/internal/app/IAppOpsService;->resetAllModes(ILjava/lang/String;)V
Lcom/android/internal/app/IAppOpsService;->setMode(IILjava/lang/String;I)V
Lcom/android/internal/app/IBatteryStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Lcom/android/internal/app/IBatteryStats$Stub;-><init>()V
Lcom/android/internal/app/IBatteryStats$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IBatteryStats;
Lcom/android/internal/app/IBatteryStats;->computeChargeTimeRemaining()J
Lcom/android/internal/app/IBatteryStats;->getAwakeTimeBattery()J
Lcom/android/internal/app/IBatteryStats;->getStatistics()[B
Lcom/android/internal/app/IBatteryStats;->isCharging()Z
-Lcom/android/internal/app/IBatteryStats;->noteFullWifiLockAcquired(I)V
-Lcom/android/internal/app/IBatteryStats;->noteFullWifiLockReleased(I)V
-Lcom/android/internal/app/IBatteryStats;->notePhoneDataConnectionState(IZ)V
-Lcom/android/internal/app/IBatteryStats;->notePhoneOff()V
-Lcom/android/internal/app/IBatteryStats;->notePhoneOn()V
-Lcom/android/internal/app/IBatteryStats;->notePhoneSignalStrength(Landroid/telephony/SignalStrength;)V
-Lcom/android/internal/app/IBatteryStats;->notePhoneState(I)V
-Lcom/android/internal/app/IBatteryStats;->noteScreenBrightness(I)V
-Lcom/android/internal/app/IBatteryStats;->noteStartSensor(II)V
-Lcom/android/internal/app/IBatteryStats;->noteStopSensor(II)V
-Lcom/android/internal/app/IBatteryStats;->noteUserActivity(II)V
-Lcom/android/internal/app/IBatteryStats;->noteWifiMulticastDisabled(I)V
-Lcom/android/internal/app/IBatteryStats;->noteWifiMulticastEnabled(I)V
Lcom/android/internal/app/IMediaContainerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IMediaContainerService;
Lcom/android/internal/app/IVoiceInteractionManagerService$Stub$Proxy;->showSessionFromSession(Landroid/os/IBinder;Landroid/os/Bundle;I)Z
Lcom/android/internal/app/IVoiceInteractionManagerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IVoiceInteractionManagerService;
Lcom/android/internal/app/IVoiceInteractionManagerService;->getKeyphraseSoundModel(ILjava/lang/String;)Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel;
-Lcom/android/internal/appwidget/IAppWidgetHost;->providerChanged(ILandroid/appwidget/AppWidgetProviderInfo;)V
-Lcom/android/internal/appwidget/IAppWidgetHost;->updateAppWidget(ILandroid/widget/RemoteViews;)V
-Lcom/android/internal/appwidget/IAppWidgetService$Stub;-><init>()V
Lcom/android/internal/appwidget/IAppWidgetService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/appwidget/IAppWidgetService;
Lcom/android/internal/appwidget/IAppWidgetService$Stub;->TRANSACTION_bindAppWidgetId:I
Lcom/android/internal/appwidget/IAppWidgetService;->bindAppWidgetId(Ljava/lang/String;IILandroid/content/ComponentName;Landroid/os/Bundle;)Z
@@ -1836,15 +1774,6 @@
Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetIds(Landroid/content/ComponentName;)[I
Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetViews(Ljava/lang/String;I)Landroid/widget/RemoteViews;
Lcom/android/internal/backup/IBackupTransport$Stub;-><init>()V
-Lcom/android/internal/backup/IBackupTransport$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/backup/IBackupTransport;
-Lcom/android/internal/backup/IBackupTransport;->clearBackupData(Landroid/content/pm/PackageInfo;)I
-Lcom/android/internal/backup/IBackupTransport;->finishBackup()I
-Lcom/android/internal/backup/IBackupTransport;->finishRestore()V
-Lcom/android/internal/backup/IBackupTransport;->getRestoreData(Landroid/os/ParcelFileDescriptor;)I
-Lcom/android/internal/backup/IBackupTransport;->initializeDevice()I
-Lcom/android/internal/backup/IBackupTransport;->requestBackupTime()J
-Lcom/android/internal/backup/IBackupTransport;->startRestore(J[Landroid/content/pm/PackageInfo;)I
-Lcom/android/internal/backup/IBackupTransport;->transportDirName()Ljava/lang/String;
Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
Lcom/android/internal/logging/MetricsLogger;-><init>()V
Lcom/android/internal/net/LegacyVpnInfo;-><init>()V
@@ -2116,7 +2045,6 @@
Lcom/android/internal/R$styleable;->AndroidManifest_sharedUserId:I
Lcom/android/internal/R$styleable;->AndroidManifest_versionCode:I
Lcom/android/internal/R$styleable;->AndroidManifest_versionName:I
-Lcom/android/internal/R$styleable;->AppWidgetProviderInfo:[I
Lcom/android/internal/R$styleable;->AutoCompleteTextView:[I
Lcom/android/internal/R$styleable;->CheckBoxPreference:[I
Lcom/android/internal/R$styleable;->CheckBoxPreference_disableDependentsState:I
@@ -2306,7 +2234,6 @@
Lcom/android/internal/R$styleable;->View_longClickable:I
Lcom/android/internal/R$styleable;->WallpaperPreviewInfo:[I
Lcom/android/internal/R$styleable;->Window:[I
-Lcom/android/internal/R$styleable;->WindowAnimation:[I
Lcom/android/internal/R$styleable;->Window_windowActionBarFullscreenDecorLayout:I
Lcom/android/internal/R$styleable;->Window_windowBackground:I
Lcom/android/internal/R$styleable;->Window_windowFullscreen:I
@@ -2369,15 +2296,6 @@
Lcom/android/internal/telephony/ICarrierConfigLoader;->getConfigForSubId(ILjava/lang/String;)Landroid/os/PersistableBundle;
Lcom/android/internal/telephony/IMms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IMms;
Lcom/android/internal/telephony/IPhoneStateListener$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneStateListener;
-Lcom/android/internal/telephony/IPhoneStateListener;->onCallForwardingIndicatorChanged(Z)V
-Lcom/android/internal/telephony/IPhoneStateListener;->onCallStateChanged(ILjava/lang/String;)V
-Lcom/android/internal/telephony/IPhoneStateListener;->onCellLocationChanged(Landroid/os/Bundle;)V
-Lcom/android/internal/telephony/IPhoneStateListener;->onDataActivity(I)V
-Lcom/android/internal/telephony/IPhoneStateListener;->onDataConnectionStateChanged(II)V
-Lcom/android/internal/telephony/IPhoneStateListener;->onMessageWaitingIndicatorChanged(Z)V
-Lcom/android/internal/telephony/IPhoneStateListener;->onServiceStateChanged(Landroid/telephony/ServiceState;)V
-Lcom/android/internal/telephony/IPhoneStateListener;->onSignalStrengthChanged(I)V
-Lcom/android/internal/telephony/IPhoneStateListener;->onSignalStrengthsChanged(Landroid/telephony/SignalStrength;)V
Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String;
Lcom/android/internal/telephony/IPhoneSubInfo$Stub;-><init>()V
@@ -2436,7 +2354,6 @@
Lcom/android/internal/telephony/ITelephony;->toggleRadioOnOff()V
Lcom/android/internal/telephony/ITelephony;->updateServiceLocation()V
Lcom/android/internal/telephony/ITelephonyRegistry$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Lcom/android/internal/telephony/ITelephonyRegistry$Stub;-><init>()V
Lcom/android/internal/telephony/ITelephonyRegistry$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephonyRegistry;
Lcom/android/internal/telephony/ITelephonyRegistry;->listen(Ljava/lang/String;Lcom/android/internal/telephony/IPhoneStateListener;IZ)V
Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCallForwardingChanged(Z)V
@@ -2509,21 +2426,9 @@
Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
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
-Lcom/android/internal/view/IInputMethod;->showSoftInput(ILandroid/os/ResultReceiver;)V
-Lcom/android/internal/view/IInputMethod;->unbindInput()V
-Lcom/android/internal/view/IInputMethodClient;->onBindMethod(Lcom/android/internal/view/InputBindResult;)V
-Lcom/android/internal/view/IInputMethodClient;->setUsingInputMethod(Z)V
Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
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/annotation/OWNERS b/core/java/android/annotation/OWNERS
index d6bb71b..85adfa7 100644
--- a/core/java/android/annotation/OWNERS
+++ b/core/java/android/annotation/OWNERS
@@ -1 +1,2 @@
tnorbye@google.com
+per-file UnsupportedAppUsage.java = mathewi@google.com
diff --git a/core/java/android/annotation/UnsupportedAppUsage.java b/core/java/android/annotation/UnsupportedAppUsage.java
index fbba6da..28145a0 100644
--- a/core/java/android/annotation/UnsupportedAppUsage.java
+++ b/core/java/android/annotation/UnsupportedAppUsage.java
@@ -27,15 +27,15 @@
* Indicates that a class member, that is not part of the SDK, is used by apps.
* Since the member is not part of the SDK, such use is not supported.
*
- * This annotation acts as a heads up that changing a given method or field
+ * <p>This annotation acts as a heads up that changing a given method or field
* may affect apps, potentially breaking them when the next Android version is
* released. In some cases, for members that are heavily used, this annotation
* may imply restrictions on changes to the member.
*
- * This annotation also results in access to the member being permitted by the
+ * <p>This annotation also results in access to the member being permitted by the
* runtime, with a warning being generated in debug builds.
*
- * For more details, see go/UnsupportedAppUsage.
+ * <p>For more details, see go/UnsupportedAppUsage.
*
* {@hide}
*/
@@ -53,15 +53,15 @@
/**
* Indicates that usage of this API is limited to apps based on their target SDK version.
*
- * Access to the API is allowed if the targetSdkVersion in the apps manifest is no greater than
- * this value. Access checks are performed at runtime.
+ * <p>Access to the API is allowed if the targetSdkVersion in the apps manifest is no greater
+ * than this value. Access checks are performed at runtime.
*
- * This is used to give app developers a grace period to migrate off a non-SDK interface. When
- * making Android version N, existing APIs can have a maxTargetSdk of N-1 added to them.
+ * <p>This is used to give app developers a grace period to migrate off a non-SDK interface.
+ * When making Android version N, existing APIs can have a maxTargetSdk of N-1 added to them.
* Developers must then migrate off the API when their app is updated in future, but it will
* continue working in the meantime.
*
- * Possible values are:
+ * <p>Possible values are:
* <ul>
* <li>
* {@link android.os.Build.VERSION_CODES#O} or {@link android.os.Build.VERSION_CODES#P},
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 19e399a..7d9af00 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -1343,9 +1343,11 @@
@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
@@ -1356,8 +1358,9 @@
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/database/OWNERS b/core/java/android/database/OWNERS
index 84e81e4..7e19942 100644
--- a/core/java/android/database/OWNERS
+++ b/core/java/android/database/OWNERS
@@ -1,2 +1,3 @@
-fkupolov@google.com
-omakoto@google.com
\ No newline at end of file
+omakoto@google.com
+jsharkey@android.com
+yamasani@google.com
diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java
index 7543920..3319f33 100644
--- a/core/java/android/net/IpConfiguration.java
+++ b/core/java/android/net/IpConfiguration.java
@@ -35,7 +35,7 @@
* 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 599ccb2..34e9476 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -22,6 +22,7 @@
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 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 @@
* @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 @@
* 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 @@
}
/**
+ * 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/net/OWNERS b/core/java/android/net/OWNERS
index fee91fbb..15f4913 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -9,12 +9,4 @@
satk@google.com
silberst@google.com
-per-file SSL*=flooey@google.com
-per-file SSL*=narayan@google.com
-per-file SSL*=tobiast@google.com
-per-file Uri*=flooey@google.com
-per-file Uri*=narayan@google.com
-per-file Uri*=tobiast@google.com
-per-file Url*=flooey@google.com
-per-file Url*=narayan@google.com
-per-file Url*=tobiast@google.com
+per-file SSL*, Uri*, Url* = flooey@google.com, narayan@google.com, tobiast@google.com
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 66f9408..9188894 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -916,6 +916,15 @@
* </ul>
*/
public static final int P = 28;
+
+ /**
+ * Q.
+ * <p>
+ * <em>Why? Why, to give you a taste of your future, a preview of things
+ * to come. Con permiso, Capitan. The hall is rented, the orchestra
+ * engaged. It's now time to see if you can dance.</em>
+ */
+ public static final int Q = CUR_DEVELOPMENT;
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index e4b2930..5c79d21 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -1,11 +1 @@
-per-file TextView.java = siyamed@google.com
-per-file TextView.java = nona@google.com
-per-file TextView.java = clarabayarri@google.com
-
-per-file EditText.java = siyamed@google.com
-per-file EditText.java = nona@google.com
-per-file EditText.java = clarabayarri@google.com
-
-per-file Editor.java = siyamed@google.com
-per-file Editor.java = nona@google.com
-per-file Editor.java = clarabayarri@google.com
+per-file TextView.java, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index ce79049..a365a56 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -1,12 +1,6 @@
# Camera
-per-file *Camera*,*camera* = cychen@google.com
-per-file *Camera*,*camera* = epeev@google.com
-per-file *Camera*,*camera* = etalvala@google.com
-per-file *Camera*,*camera* = shuzhenwang@google.com
-per-file *Camera*,*camera* = yinchiayeh@google.com
-per-file *Camera*,*camera* = zhijunhe@google.com
+per-file *Camera*,*camera* = cychen@google.com, epeev@google.com, etalvala@google.com
+per-file *Camera*,*camera* = shuzhenwang@google.com, yinchiayeh@google.com, zhijunhe@google.com
# Connectivity
-per-file android_net_*=ek@google.com
-per-file android_net_*=lorenzo@google.com
-per-file android_net_*=satk@google.com
+per-file android_net_* = ek@google.com, lorenzo@google.com, satk@google.com
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 823f1cc..9b138eb 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -323,6 +323,55 @@
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 @@
{ "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/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index 4ecfd4b..dfa5de6 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -29,7 +29,8 @@
}
void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
- android_namespace_t* appNamespace = android::FindNamespaceByClassLoader(env, classLoader);
+ android::NativeLoaderNamespace* appNamespace = android::FindNativeLoaderNamespaceByClassLoader(
+ env, classLoader);
ScopedUtfChars layerPathsChars(env, layerPaths);
android::GraphicsEnv::getInstance().setLayerPaths(appNamespace, layerPathsChars.c_str());
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 9aa0111..0022cf88 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -46,6 +46,7 @@
#include <unistd.h>
#include "android-base/logging.h"
+#include <android-base/properties.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <cutils/fs.h>
@@ -71,6 +72,7 @@
using android::String8;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
+using android::base::GetBoolProperty;
#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
append(StringPrintf(__VA_ARGS__))
@@ -937,12 +939,16 @@
RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
}
- // Assign system_server to the correct memory cgroup.
- // Not all devices mount /dev/memcg so check for the file first
- // to avoid unnecessarily printing errors and denials in the logs.
- if (!access("/dev/memcg/system/tasks", F_OK) &&
+ bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
+ bool per_app_memcg = GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
+ if (per_app_memcg) {
+ // Assign system_server to the correct memory cgroup.
+ // Not all devices mount /dev/memcg so check for the file first
+ // to avoid unnecessarily printing errors and denials in the logs.
+ if (!access("/dev/memcg/system/tasks", F_OK) &&
!WriteStringToFile(StringPrintf("%d", pid), "/dev/memcg/system/tasks")) {
- ALOGE("couldn't write %d to /dev/memcg/system/tasks", pid);
+ ALOGE("couldn't write %d to /dev/memcg/system/tasks", pid);
+ }
}
}
return pid;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ca204ec..632e439 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3446,6 +3446,9 @@
empty string is passed in -->
<string name="config_wlan_network_service_package" translatable="false"></string>
+ <!-- Telephony qualified networks service package name to bind to by default. -->
+ <string name="config_qualified_networks_service_package" translatable="false"></string>
+
<!-- Wear devices: Controls the radios affected by Activity Mode. -->
<string-array name="config_wearActivityModeRadios">
<item>"wifi"</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f2539e7..de5dc43 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -273,6 +273,7 @@
<java-symbol type="string" name="config_wlan_network_service_package" />
<java-symbol type="string" name="config_wwan_data_service_package" />
<java-symbol type="string" name="config_wlan_data_service_package" />
+ <java-symbol type="string" name="config_qualified_networks_service_package" />
<java-symbol type="bool" name="config_networkSamplingWakesDevice" />
<java-symbol type="bool" name="config_showMenuShortcutsWhenKeyboardPresent" />
<java-symbol type="bool" name="config_sip_wifi_only" />
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index f7a3e1a..bbec474 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -1,7 +1 @@
-per-file privapp-permissions-platform.xml = bpoiesz@google.com
-per-file privapp-permissions-platform.xml = fkupolov@google.com
-per-file privapp-permissions-platform.xml = hackbod@android.com
-per-file privapp-permissions-platform.xml = jsharkey@android.com
-per-file privapp-permissions-platform.xml = svetoslavganov@google.com
-per-file privapp-permissions-platform.xml = toddke@google.com
-per-file privapp-permissions-platform.xml = yamasani@google.com
+per-file privapp-permissions-platform.xml = hackbod@android.com, jsharkey@android.com, svetoslavganov@google.com, toddke@google.com, yamasani@google.com
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index b37f2cf..f2766d6 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -39,7 +39,7 @@
virtual void onError(const std::string& message) = 0;
protected:
- ~ErrorHandler() {}
+ virtual ~ErrorHandler() {}
};
class TreeObserver {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 689f518..e9c2649 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -59,7 +59,7 @@
virtual void doFrame() = 0;
protected:
- ~IFrameCallback() {}
+ virtual ~IFrameCallback() {}
};
struct VsyncSource {
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 54541f0..7cf8828 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -43,27 +43,27 @@
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! (var), "Unable to find class " className);
+ LOG_FATAL_IF(! (var), "Unable to find class %s", className);
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
- LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
+ LOG_FATAL_IF(! (var), "Unable to find field %s", fieldName);
#define GET_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \
- LOG_FATAL_IF(! (var), "Unable to find method " fieldName);
+ LOG_FATAL_IF(! (var), "Unable to find method %s", fieldName);
#define GET_STATIC_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetStaticFieldID(clazz, fieldName, fieldDescriptor); \
- LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
+ LOG_FATAL_IF(! (var), "Unable to find field %s", fieldName);
#define GET_STATIC_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetStaticMethodID(clazz, fieldName, fieldDescriptor); \
- LOG_FATAL_IF(! (var), "Unable to find static method " fieldName);
+ LOG_FATAL_IF(! (var), "Unable to find static method %s", fieldName);
-#define GET_STATIC_OBJECT_FIELD(var, clazz, fieldName) \
- var = env->GetStaticObjectField(clazz, fieldName); \
- LOG_FATAL_IF(! (var), "Unable to find static object field " fieldName);
+#define GET_STATIC_OBJECT_FIELD(var, clazz, fieldId) \
+ var = env->GetStaticObjectField(clazz, fieldId); \
+ LOG_FATAL_IF(! (var), "Unable to find static object field %p", fieldId);
struct RequestFields {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java
index f6cd990..2cb5704 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java
@@ -41,6 +41,7 @@
private Image mImage3;
@Override
+ @SuppressWarnings("CheckReturnValue")
protected void setUp() throws Exception {
super.setUp();
diff --git a/native/android/OWNERS b/native/android/OWNERS
index 067cdf8..b3f50aa 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -1,15 +1,4 @@
set noparent
-per-file libandroid_net.map.txt=codewiz@google.com
-per-file libandroid_net.map.txt=ek@google.com
-per-file libandroid_net.map.txt=jchalard@google.com
-per-file libandroid_net.map.txt=lorenzo@google.com
-per-file libandroid_net.map.txt=reminv@google.com
-per-file libandroid_net.map.txt=satk@google.com
-
-per-file net.c=codewiz@google.com
-per-file net.c=ek@google.com
-per-file net.c=jchalard@google.com
-per-file net.c=lorenzo@google.com
-per-file net.c=reminv@google.com
-per-file net.c=satk@google.com
+per-file libandroid_net.map.txt, net.c = codewiz@google.com, ek@google.com, jchalard@google.com
+per-file libandroid_net.map.txt, net.c = lorenzo@google.com, reminv@google.com, satk@google.com
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 505cfea..aa8cae5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -27,7 +27,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.UserIcons;
import com.android.settingslib.drawable.UserIconDrawable;
-import com.android.settingslib.wrapper.LocationManagerWrapper;
+
import java.text.NumberFormat;
public class Utils {
@@ -69,8 +69,7 @@
intent, UserHandle.of(userId), android.Manifest.permission.WRITE_SECURE_SETTINGS);
LocationManager locationManager =
(LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
- LocationManagerWrapper wrapper = new LocationManagerWrapper(locationManager);
- wrapper.setLocationEnabledForUser(enabled, UserHandle.of(userId));
+ locationManager.setLocationEnabledForUser(enabled, UserHandle.of(userId));
}
public static boolean updateLocationMode(Context context, int oldMode, int newMode, int userId,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 65535b5..541877c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -20,7 +20,6 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothCodecConfig;
-import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
@@ -28,9 +27,7 @@
import android.os.ParcelUuid;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
-import com.android.settingslib.wrapper.BluetoothA2dpWrapper;
import java.util.ArrayList;
import java.util.Arrays;
@@ -43,7 +40,6 @@
private Context mContext;
private BluetoothA2dp mService;
- private BluetoothA2dpWrapper mServiceWrapper;
private boolean mIsProfileReady;
private final LocalBluetoothAdapter mLocalAdapter;
@@ -67,7 +63,6 @@
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (V) Log.d(TAG,"Bluetooth service connected");
mService = (BluetoothA2dp) proxy;
- mServiceWrapper = new BluetoothA2dpWrapper(mService);
// We just bound to the service, so refresh the UI for any connected A2DP devices.
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
while (!deviceList.isEmpty()) {
@@ -110,11 +105,6 @@
BluetoothProfile.A2DP);
}
- @VisibleForTesting
- void setBluetoothA2dpWrapper(BluetoothA2dpWrapper wrapper) {
- mServiceWrapper = wrapper;
- }
-
public boolean isConnectable() {
return true;
}
@@ -194,12 +184,12 @@
}
public boolean supportsHighQualityAudio(BluetoothDevice device) {
- int support = mServiceWrapper.supportsOptionalCodecs(device);
+ int support = mService.supportsOptionalCodecs(device);
return support == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED;
}
public boolean isHighQualityAudioEnabled(BluetoothDevice device) {
- int enabled = mServiceWrapper.getOptionalCodecsEnabled(device);
+ int enabled = mService.getOptionalCodecsEnabled(device);
if (enabled != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN) {
return enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED;
} else if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED &&
@@ -210,8 +200,8 @@
return true;
}
BluetoothCodecConfig codecConfig = null;
- if (mServiceWrapper.getCodecStatus(device) != null) {
- codecConfig = mServiceWrapper.getCodecStatus(device).getCodecConfig();
+ if (mService.getCodecStatus(device) != null) {
+ codecConfig = mService.getCodecStatus(device).getCodecConfig();
}
if (codecConfig != null) {
return !codecConfig.isMandatoryCodec();
@@ -224,7 +214,7 @@
int prefValue = enabled
? BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED
: BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED;
- mServiceWrapper.setOptionalCodecsEnabled(device, prefValue);
+ mService.setOptionalCodecsEnabled(device, prefValue);
if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) {
return;
}
@@ -244,8 +234,8 @@
// We want to get the highest priority codec, since that's the one that will be used with
// this device, and see if it is high-quality (ie non-mandatory).
BluetoothCodecConfig[] selectable = null;
- if (mServiceWrapper.getCodecStatus(device) != null) {
- selectable = mServiceWrapper.getCodecStatus(device).getCodecsSelectableCapabilities();
+ if (mService.getCodecStatus(device) != null) {
+ selectable = mService.getCodecStatus(device).getCodecsSelectableCapabilities();
// To get the highest priority, we sort in reverse.
Arrays.sort(selectable,
(a, b) -> {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 0f0e4e57..3549abc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -34,7 +34,6 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import android.widget.RemoteViews;
import java.util.ArrayList;
import java.util.Collections;
@@ -96,11 +95,7 @@
/**
* The key used to get the category from metadata of activities of action
* {@link #EXTRA_SETTINGS_ACTION}
- * The value must be one of:
- * <li>com.android.settings.category.wireless</li>
- * <li>com.android.settings.category.device</li>
- * <li>com.android.settings.category.personal</li>
- * <li>com.android.settings.category.system</li>
+ * The value must be from {@link CategoryKey}.
*/
private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
@@ -171,17 +166,6 @@
public static final String META_DATA_PREFERENCE_SUMMARY_URI =
"com.android.settings.summary_uri";
- /**
- * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
- * custom view which should be displayed for the preference. The custom view will be inflated
- * as a remote view.
- *
- * This also can be used with {@link #META_DATA_PREFERENCE_SUMMARY_URI}, by setting the id
- * of the summary TextView to '@android:id/summary'.
- */
- public static final String META_DATA_PREFERENCE_CUSTOM_VIEW =
- "com.android.settings.custom_view";
-
public static final String SETTING_PKG = "com.android.settings";
/**
@@ -442,11 +426,6 @@
keyHint = metaData.getString(META_DATA_PREFERENCE_KEYHINT);
}
}
- if (metaData.containsKey(META_DATA_PREFERENCE_CUSTOM_VIEW)) {
- int layoutId = metaData.getInt(META_DATA_PREFERENCE_CUSTOM_VIEW);
- tile.remoteViews = new RemoteViews(applicationInfo.packageName, layoutId);
- updateSummaryAndTitle(context, providerMap, tile);
- }
}
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
if (DEBUG) Log.d(LOG_TAG, "Couldn't find info", e);
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java
deleted file mode 100644
index 19e556a..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java
+++ /dev/null
@@ -1,25 +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.settingslib.suggestions;
-
-public class SuggestionCategory {
- public String category;
- public String pkg;
- public boolean multiple;
- public boolean exclusive;
- public long exclusiveExpireDaysInMillis;
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java
deleted file mode 100644
index a890920..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java
+++ /dev/null
@@ -1,85 +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.settingslib.suggestions;
-
-import android.content.Intent;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import com.android.settingslib.drawer.Tile;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class SuggestionList {
- // Category -> list of suggestion map
- private final Map<SuggestionCategory, List<Tile>> mSuggestions;
-
- // A flatten list of all suggestions.
- private List<Tile> mSuggestionList;
-
- public SuggestionList() {
- mSuggestions = new ArrayMap<>();
- }
-
- public void addSuggestions(SuggestionCategory category, List<Tile> suggestions) {
- mSuggestions.put(category, suggestions);
- }
-
- public List<Tile> getSuggestions() {
- if (mSuggestionList != null) {
- return mSuggestionList;
- }
- mSuggestionList = new ArrayList<>();
- for (List<Tile> suggestions : mSuggestions.values()) {
- mSuggestionList.addAll(suggestions);
- }
- dedupeSuggestions(mSuggestionList);
- return mSuggestionList;
- }
-
- public boolean isExclusiveSuggestionCategory() {
- if (mSuggestions.size() != 1) {
- // If there is no category, or more than 1 category, it's not exclusive by definition.
- return false;
- }
- for (SuggestionCategory category : mSuggestions.keySet()) {
- if (category.exclusive) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Filter suggestions list so they are all unique.
- */
- private void dedupeSuggestions(List<Tile> suggestions) {
- final Set<String> intents = new ArraySet<>();
- for (int i = suggestions.size() - 1; i >= 0; i--) {
- final Tile suggestion = suggestions.get(i);
- final String intentUri = suggestion.intent.toUri(Intent.URI_INTENT_SCHEME);
- if (intents.contains(intentUri)) {
- suggestions.remove(i);
- } else {
- intents.add(intentUri);
- }
- }
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
deleted file mode 100644
index 9c34763..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
+++ /dev/null
@@ -1,498 +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.settingslib.suggestions;
-
-import android.Manifest;
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.annotation.RequiresPermission;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.support.annotation.VisibleForTesting;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.ArrayMap;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Xml;
-import android.view.InflateException;
-
-import com.android.settingslib.drawer.Tile;
-import com.android.settingslib.drawer.TileUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-public class SuggestionParser {
-
- private static final String TAG = "SuggestionParser";
-
- // If defined, only returns this suggestion if the feature is supported.
- public static final String META_DATA_REQUIRE_FEATURE = "com.android.settings.require_feature";
-
- // If defined, only display this optional step if an account of that type exists.
- private static final String META_DATA_REQUIRE_ACCOUNT = "com.android.settings.require_account";
-
- // If defined and not true, do not should optional step.
- private static final String META_DATA_IS_SUPPORTED = "com.android.settings.is_supported";
-
- // If defined, only display this optional step if the current user is of that type.
- private static final String META_DATA_REQUIRE_USER_TYPE =
- "com.android.settings.require_user_type";
-
- // If defined, only display this optional step if a connection is available.
- private static final String META_DATA_IS_CONNECTION_REQUIRED =
- "com.android.settings.require_connection";
-
- // The valid values that setup wizard recognizes for differentiating user types.
- private static final String META_DATA_PRIMARY_USER_TYPE_VALUE = "primary";
- private static final String META_DATA_ADMIN_USER_TYPE_VALUE = "admin";
- private static final String META_DATA_GUEST_USER_TYPE_VALUE = "guest";
- private static final String META_DATA_RESTRICTED_USER_TYPE_VALUE = "restricted";
-
- /**
- * Allows suggestions to appear after a certain number of days, and to re-appear if dismissed.
- * For instance:
- * 0,10
- * Will appear immediately, but if the user removes it, it will come back after 10 days.
- *
- * Another example:
- * 10,30
- * Will only show up after 10 days, and then again after 30.
- */
- public static final String META_DATA_DISMISS_CONTROL = "com.android.settings.dismiss";
-
- // Shared prefs keys for storing dismissed state.
- // Index into current dismissed state.
- public static final String SETUP_TIME = "_setup_time";
- private static final String IS_DISMISSED = "_is_dismissed";
-
- // Default dismiss control for smart suggestions.
- private static final String DEFAULT_SMART_DISMISS_CONTROL = "0";
-
- private final Context mContext;
- private final List<SuggestionCategory> mSuggestionList;
- private final ArrayMap<Pair<String, String>, Tile> mAddCache = new ArrayMap<>();
- private final SharedPreferences mSharedPrefs;
- private final String mDefaultDismissControl;
-
- public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml,
- String defaultDismissControl) {
- this(
- context,
- sharedPrefs,
- (List<SuggestionCategory>) new SuggestionOrderInflater(context).parse(orderXml),
- defaultDismissControl);
- }
-
- public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) {
- this(context, sharedPrefs, orderXml, DEFAULT_SMART_DISMISS_CONTROL);
- }
-
- @VisibleForTesting
- public SuggestionParser(
- Context context,
- SharedPreferences sharedPrefs,
- List<SuggestionCategory> suggestionList,
- String defaultDismissControl) {
- mContext = context;
- mSuggestionList = suggestionList;
- mSharedPrefs = sharedPrefs;
- mDefaultDismissControl = defaultDismissControl;
- }
-
- public SuggestionList getSuggestions(boolean isSmartSuggestionEnabled) {
- final SuggestionList suggestionList = new SuggestionList();
- final int N = mSuggestionList.size();
- for (int i = 0; i < N; i++) {
- final SuggestionCategory category = mSuggestionList.get(i);
- if (category.exclusive && !isExclusiveCategoryExpired(category)) {
- // If suggestions from an exclusive category are present, parsing is stopped
- // and only suggestions from that category are displayed. Note that subsequent
- // exclusive categories are also ignored.
- final List<Tile> exclusiveSuggestions = new ArrayList<>();
-
- // Read suggestion and force isSmartSuggestion to be false so the rule defined
- // from each suggestion itself is used.
- readSuggestions(category, exclusiveSuggestions, false /* isSmartSuggestion */);
- if (!exclusiveSuggestions.isEmpty()) {
- final SuggestionList exclusiveList = new SuggestionList();
- exclusiveList.addSuggestions(category, exclusiveSuggestions);
- return exclusiveList;
- }
- } else {
- // Either the category is not exclusive, or the exclusiveness expired so we should
- // treat it as a normal category.
- final List<Tile> suggestions = new ArrayList<>();
- readSuggestions(category, suggestions, isSmartSuggestionEnabled);
- suggestionList.addSuggestions(category, suggestions);
- }
- }
- return suggestionList;
- }
-
- /**
- * Dismisses a suggestion, returns true if the suggestion has no more dismisses left and should
- * be disabled.
- */
- public boolean dismissSuggestion(Tile suggestion) {
- final String keyBase = suggestion.intent.getComponent().flattenToShortString();
- mSharedPrefs.edit()
- .putBoolean(keyBase + IS_DISMISSED, true)
- .commit();
- return true;
- }
-
- @VisibleForTesting
- public void filterSuggestions(
- List<Tile> suggestions, int countBefore, boolean isSmartSuggestionEnabled) {
- for (int i = countBefore; i < suggestions.size(); i++) {
- if (!isAvailable(suggestions.get(i)) ||
- !isSupported(suggestions.get(i)) ||
- !satisifesRequiredUserType(suggestions.get(i)) ||
- !satisfiesRequiredAccount(suggestions.get(i)) ||
- !satisfiesConnectivity(suggestions.get(i)) ||
- isDismissed(suggestions.get(i), isSmartSuggestionEnabled)) {
- suggestions.remove(i--);
- }
- }
- }
-
- @VisibleForTesting
- void readSuggestions(
- SuggestionCategory category, List<Tile> suggestions, boolean isSmartSuggestionEnabled) {
- int countBefore = suggestions.size();
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(category.category);
- if (category.pkg != null) {
- intent.setPackage(category.pkg);
- }
- TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent,
- mAddCache, null, suggestions, true, false, false, true /* shouldUpdateTiles */);
- filterSuggestions(suggestions, countBefore, isSmartSuggestionEnabled);
- if (!category.multiple && suggestions.size() > (countBefore + 1)) {
- // If there are too many, remove them all and only re-add the one with the highest
- // priority.
- Tile item = suggestions.remove(suggestions.size() - 1);
- while (suggestions.size() > countBefore) {
- Tile last = suggestions.remove(suggestions.size() - 1);
- if (last.priority > item.priority) {
- item = last;
- }
- }
- // If category is marked as done, do not add any item.
- if (!isCategoryDone(category.category)) {
- suggestions.add(item);
- }
- }
- }
-
- private boolean isAvailable(Tile suggestion) {
- final String featuresRequired = suggestion.metaData.getString(META_DATA_REQUIRE_FEATURE);
- if (featuresRequired != null) {
- for (String feature : featuresRequired.split(",")) {
- if (TextUtils.isEmpty(feature)) {
- Log.w(TAG, "Found empty substring when parsing required features: "
- + featuresRequired);
- } else if (!mContext.getPackageManager().hasSystemFeature(feature)) {
- Log.i(TAG, suggestion.title + " requires unavailable feature " + feature);
- return false;
- }
- }
- }
- return true;
- }
-
- @RequiresPermission(Manifest.permission.MANAGE_USERS)
- private boolean satisifesRequiredUserType(Tile suggestion) {
- final String requiredUser = suggestion.metaData.getString(META_DATA_REQUIRE_USER_TYPE);
- if (requiredUser != null) {
- final UserManager userManager = mContext.getSystemService(UserManager.class);
- UserInfo userInfo = userManager.getUserInfo(UserHandle.myUserId());
- for (String userType : requiredUser.split("\\|")) {
- final boolean primaryUserCondtionMet = userInfo.isPrimary()
- && META_DATA_PRIMARY_USER_TYPE_VALUE.equals(userType);
- final boolean adminUserConditionMet = userInfo.isAdmin()
- && META_DATA_ADMIN_USER_TYPE_VALUE.equals(userType);
- final boolean guestUserCondtionMet = userInfo.isGuest()
- && META_DATA_GUEST_USER_TYPE_VALUE.equals(userType);
- final boolean restrictedUserCondtionMet = userInfo.isRestricted()
- && META_DATA_RESTRICTED_USER_TYPE_VALUE.equals(userType);
- if (primaryUserCondtionMet || adminUserConditionMet || guestUserCondtionMet
- || restrictedUserCondtionMet) {
- return true;
- }
- }
- Log.i(TAG, suggestion.title + " requires user type " + requiredUser);
- return false;
- }
- return true;
- }
-
- public boolean satisfiesRequiredAccount(Tile suggestion) {
- final String requiredAccountType = suggestion.metaData.getString(META_DATA_REQUIRE_ACCOUNT);
- if (requiredAccountType == null) {
- return true;
- }
- AccountManager accountManager = mContext.getSystemService(AccountManager.class);
- Account[] accounts = accountManager.getAccountsByType(requiredAccountType);
- boolean satisfiesRequiredAccount = accounts.length > 0;
- if (!satisfiesRequiredAccount) {
- Log.i(TAG, suggestion.title + " requires unavailable account type "
- + requiredAccountType);
- }
- return satisfiesRequiredAccount;
- }
-
- public boolean isSupported(Tile suggestion) {
- final int isSupportedResource = suggestion.metaData.getInt(META_DATA_IS_SUPPORTED);
- try {
- if (suggestion.intent == null) {
- return false;
- }
- final Resources res = mContext.getPackageManager().getResourcesForActivity(
- suggestion.intent.getComponent());
- boolean isSupported =
- isSupportedResource != 0 ? res.getBoolean(isSupportedResource) : true;
- if (!isSupported) {
- Log.i(TAG, suggestion.title + " requires unsupported resource "
- + isSupportedResource);
- }
- return isSupported;
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent());
- return false;
- } catch (Resources.NotFoundException e) {
- Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent(), e);
- return false;
- }
- }
-
- private boolean satisfiesConnectivity(Tile suggestion) {
- final boolean isConnectionRequired =
- suggestion.metaData.getBoolean(META_DATA_IS_CONNECTION_REQUIRED);
- if (!isConnectionRequired) {
- return true;
- }
- ConnectivityManager cm =
- (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo netInfo = cm.getActiveNetworkInfo();
- boolean satisfiesConnectivity = netInfo != null && netInfo.isConnectedOrConnecting();
- if (!satisfiesConnectivity) {
- Log.i(TAG, suggestion.title + " is missing required connection.");
- }
- return satisfiesConnectivity;
- }
-
- public boolean isCategoryDone(String category) {
- String name = Settings.Secure.COMPLETED_CATEGORY_PREFIX + category;
- return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) != 0;
- }
-
- public void markCategoryDone(String category) {
- String name = Settings.Secure.COMPLETED_CATEGORY_PREFIX + category;
- Settings.Secure.putInt(mContext.getContentResolver(), name, 1);
- }
-
- /**
- * Whether or not the category's exclusiveness has expired.
- */
- private boolean isExclusiveCategoryExpired(SuggestionCategory category) {
- final String keySetupTime = category.category + SETUP_TIME;
- final long currentTime = System.currentTimeMillis();
- if (!mSharedPrefs.contains(keySetupTime)) {
- mSharedPrefs.edit()
- .putLong(keySetupTime, currentTime)
- .commit();
- }
- if (category.exclusiveExpireDaysInMillis < 0) {
- // negative means never expires
- return false;
- }
- final long setupTime = mSharedPrefs.getLong(keySetupTime, 0);
- final long elapsedTime = currentTime - setupTime;
- Log.d(TAG, "Day " + elapsedTime / DateUtils.DAY_IN_MILLIS + " for " + category.category);
- return elapsedTime > category.exclusiveExpireDaysInMillis;
- }
-
- @VisibleForTesting
- boolean isDismissed(Tile suggestion, boolean isSmartSuggestionEnabled) {
- String dismissControl = getDismissControl(suggestion, isSmartSuggestionEnabled);
- String keyBase = suggestion.intent.getComponent().flattenToShortString();
- if (!mSharedPrefs.contains(keyBase + SETUP_TIME)) {
- mSharedPrefs.edit()
- .putLong(keyBase + SETUP_TIME, System.currentTimeMillis())
- .commit();
- }
- // Check if it's already manually dismissed
- final boolean isDismissed = mSharedPrefs.getBoolean(keyBase + IS_DISMISSED, false);
- if (isDismissed) {
- return true;
- }
- if (dismissControl == null) {
- return false;
- }
- // Parse when suggestion should first appear. return true to artificially hide suggestion
- // before then.
- int firstAppearDay = parseDismissString(dismissControl);
- long firstAppearDayInMs = getEndTime(mSharedPrefs.getLong(keyBase + SETUP_TIME, 0),
- firstAppearDay);
- if (System.currentTimeMillis() >= firstAppearDayInMs) {
- // Dismiss timeout has passed, undismiss it.
- mSharedPrefs.edit()
- .putBoolean(keyBase + IS_DISMISSED, false)
- .commit();
- return false;
- }
- return true;
- }
-
- private long getEndTime(long startTime, int daysDelay) {
- long days = daysDelay * DateUtils.DAY_IN_MILLIS;
- return startTime + days;
- }
-
- /**
- * Parse the first int from a string formatted as "0,1,2..."
- * The value means suggestion should first appear on Day X.
- */
- private int parseDismissString(String dismissControl) {
- final String[] dismissStrs = dismissControl.split(",");
- return Integer.parseInt(dismissStrs[0]);
- }
-
- private String getDismissControl(Tile suggestion, boolean isSmartSuggestionEnabled) {
- if (isSmartSuggestionEnabled) {
- return mDefaultDismissControl;
- } else {
- return suggestion.metaData.getString(META_DATA_DISMISS_CONTROL);
- }
- }
-
- private static class SuggestionOrderInflater {
- private static final String TAG_LIST = "optional-steps";
- private static final String TAG_ITEM = "step";
-
- private static final String ATTR_CATEGORY = "category";
- private static final String ATTR_PACKAGE = "package";
- private static final String ATTR_MULTIPLE = "multiple";
- private static final String ATTR_EXCLUSIVE = "exclusive";
- private static final String ATTR_EXCLUSIVE_EXPIRE_DAYS = "exclusiveExpireDays";
-
- private final Context mContext;
-
- public SuggestionOrderInflater(Context context) {
- mContext = context;
- }
-
- public Object parse(int resource) {
- XmlPullParser parser = mContext.getResources().getXml(resource);
- final AttributeSet attrs = Xml.asAttributeSet(parser);
- try {
- // Look for the root node.
- int type;
- do {
- type = parser.next();
- } while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT);
-
- if (type != XmlPullParser.START_TAG) {
- throw new InflateException(parser.getPositionDescription()
- + ": No start tag found!");
- }
-
- // Temp is the root that was found in the xml
- Object xmlRoot = onCreateItem(parser.getName(), attrs);
-
- // Inflate all children under temp
- rParse(parser, xmlRoot, attrs);
- return xmlRoot;
- } catch (XmlPullParserException | IOException e) {
- Log.w(TAG, "Problem parser resource " + resource, e);
- return null;
- }
- }
-
- /**
- * Recursive method used to descend down the xml hierarchy and instantiate
- * items, instantiate their children.
- */
- private void rParse(XmlPullParser parser, Object parent, final AttributeSet attrs)
- throws XmlPullParserException, IOException {
- final int depth = parser.getDepth();
-
- int type;
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
-
- final String name = parser.getName();
-
- Object item = onCreateItem(name, attrs);
- onAddChildItem(parent, item);
- rParse(parser, item, attrs);
- }
- }
-
- protected void onAddChildItem(Object parent, Object child) {
- if (parent instanceof List<?> && child instanceof SuggestionCategory) {
- ((List<SuggestionCategory>) parent).add((SuggestionCategory) child);
- } else {
- throw new IllegalArgumentException("Parent was not a list");
- }
- }
-
- protected Object onCreateItem(String name, AttributeSet attrs) {
- if (name.equals(TAG_LIST)) {
- return new ArrayList<SuggestionCategory>();
- } else if (name.equals(TAG_ITEM)) {
- SuggestionCategory category = new SuggestionCategory();
- category.category = attrs.getAttributeValue(null, ATTR_CATEGORY);
- category.pkg = attrs.getAttributeValue(null, ATTR_PACKAGE);
- String multiple = attrs.getAttributeValue(null, ATTR_MULTIPLE);
- category.multiple = !TextUtils.isEmpty(multiple) && Boolean.parseBoolean(multiple);
- String exclusive = attrs.getAttributeValue(null, ATTR_EXCLUSIVE);
- category.exclusive =
- !TextUtils.isEmpty(exclusive) && Boolean.parseBoolean(exclusive);
- String expireDaysAttr = attrs.getAttributeValue(null,
- ATTR_EXCLUSIVE_EXPIRE_DAYS);
- long expireDays = !TextUtils.isEmpty(expireDaysAttr)
- ? Integer.parseInt(expireDaysAttr)
- : -1;
- category.exclusiveExpireDaysInMillis = DateUtils.DAY_IN_MILLIS * expireDays;
- return category;
- } else {
- throw new IllegalArgumentException("Unknown item " + name);
- }
- }
- }
-}
-
diff --git a/packages/SettingsLib/src/com/android/settingslib/wrapper/BluetoothA2dpWrapper.java b/packages/SettingsLib/src/com/android/settingslib/wrapper/BluetoothA2dpWrapper.java
deleted file mode 100644
index 17e3401..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/wrapper/BluetoothA2dpWrapper.java
+++ /dev/null
@@ -1,70 +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.settingslib.wrapper;
-
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothCodecStatus;
-import android.bluetooth.BluetoothDevice;
-
-/**
- * This class replicates some methods of android.bluetooth.BluetoothA2dp that are new and not
- * yet available in our current version of Robolectric. It provides a thin wrapper to call the real
- * methods in production and a mock in tests.
- */
-public class BluetoothA2dpWrapper {
-
- private BluetoothA2dp mService;
-
- public BluetoothA2dpWrapper(BluetoothA2dp service) {
- mService = service;
- }
-
- /**
- * @return the real {@code BluetoothA2dp} object
- */
- public BluetoothA2dp getService() {
- return mService;
- }
-
- /**
- * Wraps {@code BluetoothA2dp.getCodecStatus}
- */
- public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
- return mService.getCodecStatus(device);
- }
-
- /**
- * Wraps {@code BluetoothA2dp.supportsOptionalCodecs}
- */
- public int supportsOptionalCodecs(BluetoothDevice device) {
- return mService.supportsOptionalCodecs(device);
- }
-
- /**
- * Wraps {@code BluetoothA2dp.getOptionalCodecsEnabled}
- */
- public int getOptionalCodecsEnabled(BluetoothDevice device) {
- return mService.getOptionalCodecsEnabled(device);
- }
-
- /**
- * Wraps {@code BluetoothA2dp.setOptionalCodecsEnabled}
- */
- public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
- mService.setOptionalCodecsEnabled(device, value);
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wrapper/LocationManagerWrapper.java b/packages/SettingsLib/src/com/android/settingslib/wrapper/LocationManagerWrapper.java
deleted file mode 100644
index 1a268a6..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/wrapper/LocationManagerWrapper.java
+++ /dev/null
@@ -1,64 +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.settingslib.wrapper;
-
-import android.location.LocationManager;
-import android.os.UserHandle;
-
-/**
- * This class replicates some methods of android.location.LocationManager that are new and not
- * yet available in our current version of Robolectric. It provides a thin wrapper to call the real
- * methods in production and a mock in tests.
- */
-public class LocationManagerWrapper {
-
- private LocationManager mLocationManager;
-
- public LocationManagerWrapper(LocationManager locationManager) {
- mLocationManager = locationManager;
- }
-
- /** Returns the real {@code LocationManager} object */
- public LocationManager getLocationManager() {
- return mLocationManager;
- }
-
- /** Wraps {@code LocationManager.isProviderEnabled} method */
- public boolean isProviderEnabled(String provider) {
- return mLocationManager.isProviderEnabled(provider);
- }
-
- /** Wraps {@code LocationManager.setProviderEnabledForUser} method */
- public void setProviderEnabledForUser(String provider, boolean enabled, UserHandle userHandle) {
- mLocationManager.setProviderEnabledForUser(provider, enabled, userHandle);
- }
-
- /** Wraps {@code LocationManager.isLocationEnabled} method */
- public boolean isLocationEnabled() {
- return mLocationManager.isLocationEnabled();
- }
-
- /** Wraps {@code LocationManager.isLocationEnabledForUser} method */
- public boolean isLocationEnabledForUser(UserHandle userHandle) {
- return mLocationManager.isLocationEnabledForUser(userHandle);
- }
-
- /** Wraps {@code LocationManager.setLocationEnabledForUser} method */
- public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
- mLocationManager.setLocationEnabledForUser(enabled, userHandle);
- }
-}
diff --git a/packages/SettingsLib/tests/robotests/res/xml/suggestion_ordering.xml b/packages/SettingsLib/tests/robotests/res/xml/suggestion_ordering.xml
deleted file mode 100644
index f02ac15..0000000
--- a/packages/SettingsLib/tests/robotests/res/xml/suggestion_ordering.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<optional-steps>
- <step category="com.android.settings.suggested.category.DEFERRED_SETUP"
- exclusive="true" />
- <step category="com.android.settings.suggested.category.LOCK_SCREEN" />
- <step category="com.android.settings.suggested.category.TRUST_AGENT" />
- <step category="com.android.settings.suggested.category.EMAIL" />
- <step category="com.android.settings.suggested.category.PARTNER_ACCOUNT"
- multiple="true" />
- <step category="com.android.settings.suggested.category.GESTURE" />
- <step category="com.android.settings.suggested.category.HOTWORD" />
- <step category="com.android.settings.suggested.category.DEFAULT"
- multiple="true" />
- <step category="com.android.settings.suggested.category.SETTINGS_ONLY"
- multiple="true" />
-</optional-steps>
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index a79f841..09a2bd2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -41,8 +41,6 @@
import android.provider.Settings.Secure;
import android.text.TextUtils;
-import com.android.settingslib.wrapper.LocationManagerWrapper;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -63,7 +61,7 @@
@RunWith(SettingsLibRobolectricTestRunner.class)
@Config(shadows = {
UtilsTest.ShadowSecure.class,
- UtilsTest.ShadowLocationManagerWrapper.class})
+ UtilsTest.ShadowLocationManager.class})
public class UtilsTest {
private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
private static final String PERCENTAGE_0 = "0%";
@@ -192,8 +190,8 @@
}
}
- @Implements(value = LocationManagerWrapper.class)
- public static class ShadowLocationManagerWrapper {
+ @Implements(value = LocationManager.class)
+ public static class ShadowLocationManager {
@Implementation
public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index 334ea16..ef13a5f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -31,7 +31,6 @@
import android.content.res.Resources;
import com.android.settingslib.R;
-import com.android.settingslib.wrapper.BluetoothA2dpWrapper;
import org.junit.Before;
import org.junit.Test;
@@ -49,7 +48,6 @@
@Mock LocalBluetoothProfileManager mProfileManager;
@Mock BluetoothDevice mDevice;
@Mock BluetoothA2dp mBluetoothA2dp;
- @Mock BluetoothA2dpWrapper mBluetoothA2dpWrapper;
BluetoothProfile.ServiceListener mServiceListener;
A2dpProfile mProfile;
@@ -68,31 +66,30 @@
mProfile = new A2dpProfile(mContext, mAdapter, mDeviceManager, mProfileManager);
mServiceListener.onServiceConnected(BluetoothProfile.A2DP, mBluetoothA2dp);
- mProfile.setBluetoothA2dpWrapper(mBluetoothA2dpWrapper);
}
@Test
public void supportsHighQualityAudio() {
- when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
assertThat(mProfile.supportsHighQualityAudio(mDevice)).isTrue();
- when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse();
- when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN);
assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse();
}
@Test
public void isHighQualityAudioEnabled() {
- when(mBluetoothA2dpWrapper.getOptionalCodecsEnabled(any())).thenReturn(
+ when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue();
- when(mBluetoothA2dpWrapper.getOptionalCodecsEnabled(any())).thenReturn(
+ when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse();
@@ -100,23 +97,23 @@
// then isHighQualityAudioEnabled() should return true or false based on whether optional
// codecs are supported. If the device is connected then we should ask it directly, but if
// the device isn't connected then rely on the stored pref about such support.
- when(mBluetoothA2dpWrapper.getOptionalCodecsEnabled(any())).thenReturn(
+ when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN);
when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
BluetoothProfile.STATE_DISCONNECTED);
- when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse();
- when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue();
when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
BluetoothProfile.STATE_CONNECTED);
BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
- when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
+ when(mBluetoothA2dp.getCodecStatus(mDevice)).thenReturn(status);
BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
when(status.getCodecConfig()).thenReturn(config);
when(config.isMandatoryCodec()).thenReturn(false);
@@ -151,14 +148,14 @@
// Most tests want to simulate optional codecs being supported by the device, so do that
// by default here.
- when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
}
@Test
public void getLableCodecsNotSupported() {
setupLabelTest();
- when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(UNKNOWN_CODEC_LABEL);
}
@@ -179,7 +176,7 @@
BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
BluetoothCodecConfig[] configs = {config};
- when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
+ when(mBluetoothA2dp.getCodecStatus(mDevice)).thenReturn(status);
when(status.getCodecsSelectableCapabilities()).thenReturn(configs);
when(config.isMandatoryCodec()).thenReturn(true);
@@ -194,7 +191,7 @@
BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
BluetoothCodecConfig[] configs = {config};
- when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
+ when(mBluetoothA2dp.getCodecStatus(mDevice)).thenReturn(status);
when(status.getCodecsSelectableCapabilities()).thenReturn(configs);
when(config.isMandatoryCodec()).thenReturn(false);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index fc1b2238..6e66805 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -28,7 +28,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.RuntimeEnvironment.application;
-import static org.robolectric.shadow.api.Shadow.extract;
import android.app.ActivityManager;
import android.content.ContentResolver;
@@ -52,9 +51,6 @@
import android.util.Pair;
import android.widget.RemoteViews;
-import com.android.settingslib.R;
-import com.android.settingslib.suggestions.SuggestionParser;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -67,7 +63,6 @@
import org.robolectric.annotation.Implements;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -164,35 +159,6 @@
}
@Test
- public void getTilesForIntent_shouldSkipFilteredApps() {
- Intent intent = new Intent();
- Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
- List<Tile> outTiles = new ArrayList<>();
- List<ResolveInfo> info = new ArrayList<>();
- ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
- URI_GET_SUMMARY);
- addMetadataToInfo(resolveInfo, "com.android.settings.require_account", "com.google");
- addMetadataToInfo(resolveInfo, "com.android.settings.require_connection", "true");
- info.add(resolveInfo);
-
- when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
- .thenReturn(info);
-
- TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
- null /* defaultCategory */, outTiles, false /* usePriority */,
- false /* checkCategory */, true /* forceTintExternalIcon */);
-
- assertThat(outTiles.size()).isEqualTo(1);
- SuggestionParser parser = new SuggestionParser(
- mContext,
- null,
- Collections.emptyList(),
- "0,10");
- parser.filterSuggestions(outTiles, 0, false);
- assertThat(outTiles.size()).isEqualTo(0);
- }
-
- @Test
public void getCategories_shouldHandleExtraIntentAction() {
final String testCategory = "category1";
final String testAction = "action1";
@@ -392,108 +358,6 @@
assertThat(outTiles.size()).isEqualTo(1);
}
- @Test
- public void getTilesForIntent_shouldShowRemoteViewIfSpecified() {
- Intent intent = new Intent();
- Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
- List<Tile> outTiles = new ArrayList<>();
- List<ResolveInfo> info = new ArrayList<>();
- ResolveInfo resolveInfo = newInfo(true, null /* category */);
- resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
- R.layout.user_preference);
- info.add(resolveInfo);
-
- when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
- .thenReturn(info);
-
- TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
- null /* defaultCategory */, outTiles, false /* usePriority */,
- false /* checkCategory */, true /* forceTintExternalIcon */);
-
- assertThat(outTiles.size()).isEqualTo(1);
- Tile tile = outTiles.get(0);
- assertThat(tile.remoteViews).isNotNull();
- assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
- }
-
- @Test
- public void getTilesForIntent_summaryUriSpecified_shouldOverrideRemoteViewSummary()
- throws RemoteException {
- Intent intent = new Intent();
- Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
- List<Tile> outTiles = new ArrayList<>();
- List<ResolveInfo> info = new ArrayList<>();
- ResolveInfo resolveInfo = newInfo(true, null /* category */, null,
- null, URI_GET_SUMMARY);
- resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
- R.layout.user_preference);
- info.add(resolveInfo);
-
- when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
- .thenReturn(info);
-
- // Mock the content provider interaction.
- Bundle bundle = new Bundle();
- bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
- when(mIContentProvider.call(anyString(),
- eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
- any())).thenReturn(bundle);
- when(mContentResolver.acquireUnstableProvider(anyString()))
- .thenReturn(mIContentProvider);
- when(mContentResolver.acquireUnstableProvider(any(Uri.class)))
- .thenReturn(mIContentProvider);
-
- TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
- null /* defaultCategory */, outTiles, false /* usePriority */,
- false /* checkCategory */, true /* forceTintExternalIcon */);
-
- assertThat(outTiles.size()).isEqualTo(1);
- Tile tile = outTiles.get(0);
- assertThat(tile.remoteViews).isNotNull();
- assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
- // Make sure the summary TextView got a new text string.
- TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
- assertThat(shadowRemoteViews.overrideViewId).isEqualTo(android.R.id.summary);
- assertThat(shadowRemoteViews.overrideText).isEqualTo("new summary text");
- }
-
- @Test
- public void getTilesForIntent_providerUnavailable_shouldNotOverrideRemoteViewSummary()
- throws RemoteException {
- Intent intent = new Intent();
- Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
- List<Tile> outTiles = new ArrayList<>();
- List<ResolveInfo> info = new ArrayList<>();
- ResolveInfo resolveInfo = newInfo(true, null /* category */, null,
- null, URI_GET_SUMMARY);
- resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
- R.layout.user_preference);
- info.add(resolveInfo);
-
- when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
- .thenReturn(info);
-
- // Mock the content provider interaction.
- Bundle bundle = new Bundle();
- bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
- when(mIContentProvider.call(anyString(),
- eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
- any())).thenReturn(bundle);
-
- TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
- null /* defaultCategory */, outTiles, false /* usePriority */,
- false /* checkCategory */, true /* forceTintExternalIcon */);
-
- assertThat(outTiles.size()).isEqualTo(1);
- Tile tile = outTiles.get(0);
- assertThat(tile.remoteViews).isNotNull();
- assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
- // Make sure the summary TextView didn't get any text view updates.
- TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
- assertThat(shadowRemoteViews.overrideViewId).isNull();
- assertThat(shadowRemoteViews.overrideText).isNull();
- }
-
public static ResolveInfo newInfo(boolean systemApp, String category) {
return newInfo(systemApp, category, null);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
deleted file mode 100644
index d05bcfd..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
+++ /dev/null
@@ -1,200 +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.settingslib.suggestions;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.robolectric.RuntimeEnvironment.application;
-import static org.robolectric.shadow.api.Shadow.extract;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-import com.android.settingslib.drawer.Tile;
-import com.android.settingslib.drawer.TileUtilsTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.shadows.ShadowPackageManager;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-public class SuggestionParserTest {
-
- private ShadowPackageManager mPackageManager;
- private SuggestionParser mSuggestionParser;
- private SuggestionCategory mMultipleCategory;
- private SuggestionCategory mExclusiveCategory;
- private SuggestionCategory mExpiredExclusiveCategory;
- private List<Tile> mSuggestionsBeforeDismiss;
- private List<Tile> mSuggestionsAfterDismiss;
- private SharedPreferences mPrefs;
- private Tile mSuggestion;
-
- @Before
- public void setUp() {
- mPackageManager = extract(application.getPackageManager());
- mPrefs = PreferenceManager.getDefaultSharedPreferences(application);
- mSuggestion = new Tile();
- mSuggestion.intent = new Intent("action");
- mSuggestion.intent.setComponent(new ComponentName("pkg", "cls"));
- mSuggestion.metaData = new Bundle();
- mMultipleCategory = new SuggestionCategory();
- mMultipleCategory.category = "category1";
- mMultipleCategory.multiple = true;
- mExclusiveCategory = new SuggestionCategory();
- mExclusiveCategory.category = "category2";
- mExclusiveCategory.exclusive = true;
- mExpiredExclusiveCategory = new SuggestionCategory();
- mExpiredExclusiveCategory.category = "category3";
- mExpiredExclusiveCategory.exclusive = true;
- mExpiredExclusiveCategory.exclusiveExpireDaysInMillis = 0;
-
- mSuggestionParser = new SuggestionParser(application, mPrefs,
- Arrays.asList(mMultipleCategory, mExclusiveCategory, mExpiredExclusiveCategory),
- "0");
-
- ResolveInfo info1 = TileUtilsTest.newInfo(true, null);
- info1.activityInfo.packageName = "pkg";
- ResolveInfo infoDupe1 = TileUtilsTest.newInfo(true, null);
- infoDupe1.activityInfo.packageName = "pkg";
-
- ResolveInfo info2 = TileUtilsTest.newInfo(true, null);
- info2.activityInfo.packageName = "pkg2";
- ResolveInfo info3 = TileUtilsTest.newInfo(true, null);
- info3.activityInfo.packageName = "pkg3";
- ResolveInfo info4 = TileUtilsTest.newInfo(true, null);
- info4.activityInfo.packageName = "pkg4";
-
- Intent intent1 = new Intent(Intent.ACTION_MAIN).addCategory("category1");
- Intent intent2 = new Intent(Intent.ACTION_MAIN).addCategory("category2");
- Intent intent3 = new Intent(Intent.ACTION_MAIN).addCategory("category3");
-
- mPackageManager.addResolveInfoForIntent(intent1, info1);
- mPackageManager.addResolveInfoForIntent(intent1, info2);
- mPackageManager.addResolveInfoForIntent(intent1, infoDupe1);
- mPackageManager.addResolveInfoForIntent(intent2, info3);
- mPackageManager.addResolveInfoForIntent(intent3, info4);
- }
-
- @Test
- public void dismissSuggestion_shouldDismiss() {
- assertThat(mSuggestionParser.dismissSuggestion(mSuggestion)).isTrue();
- }
-
- @Test
- public void testGetSuggestions_withoutSmartSuggestions_shouldDismiss() {
- readAndDismissSuggestion(false);
- mSuggestionParser.readSuggestions(mMultipleCategory, mSuggestionsAfterDismiss, false);
- assertThat(mSuggestionsBeforeDismiss).hasSize(2);
- assertThat(mSuggestionsAfterDismiss).hasSize(1);
- assertThat(mSuggestionsBeforeDismiss.get(1)).isEqualTo(mSuggestionsAfterDismiss.get(0));
- }
-
- @Test
- public void testGetSuggestions_withSmartSuggestions_shouldDismiss() {
- readAndDismissSuggestion(true);
- assertThat(mSuggestionsBeforeDismiss).hasSize(2);
- assertThat(mSuggestionsAfterDismiss).hasSize(1);
- }
-
- @Test
- public void testGetSuggestion_exclusiveNotAvailable_onlyRegularCategoryAndNoDupe() {
- mPackageManager.removeResolveInfosForIntent(
- new Intent(Intent.ACTION_MAIN).addCategory("category2"),
- "pkg3");
- mPackageManager.removeResolveInfosForIntent(
- new Intent(Intent.ACTION_MAIN).addCategory("category3"),
- "pkg4");
-
- // If exclusive item is not available, the other categories should be shown
- final SuggestionList sl =
- mSuggestionParser.getSuggestions(false /* isSmartSuggestionEnabled */);
- final List<Tile> suggestions = sl.getSuggestions();
- assertThat(suggestions).hasSize(2);
-
- assertThat(suggestions.get(0).intent.getComponent().getPackageName()).isEqualTo("pkg");
- assertThat(suggestions.get(1).intent.getComponent().getPackageName()).isEqualTo("pkg2");
- }
-
- @Test
- public void testGetSuggestion_exclusiveExpiredAvailable_shouldLoadWithRegularCategory() {
- // First remove permanent exclusive
- mPackageManager.removeResolveInfosForIntent(
- new Intent(Intent.ACTION_MAIN).addCategory("category2"),
- "pkg3");
- // Set the other exclusive to be expired.
- mPrefs.edit()
- .putLong(mExpiredExclusiveCategory.category + "_setup_time",
- System.currentTimeMillis() - 1000)
- .commit();
-
- // If exclusive is expired, they should be shown together with the other categories
- final SuggestionList sl =
- mSuggestionParser.getSuggestions(true /* isSmartSuggestionEnabled */);
- final List<Tile> suggestions = sl.getSuggestions();
-
- assertThat(suggestions).hasSize(3);
- }
-
- @Test
- public void testGetSuggestions_exclusive() {
- final SuggestionList sl =
- mSuggestionParser.getSuggestions(false /* isSmartSuggestionEnabled */);
- final List<Tile> suggestions = sl.getSuggestions();
-
- assertThat(suggestions).hasSize(1);
- }
-
- @Test
- public void isSuggestionDismissed_dismissedSuggestion_shouldReturnTrue() {
- final Tile suggestion = new Tile();
- suggestion.metaData = new Bundle();
- suggestion.metaData.putString(SuggestionParser.META_DATA_DISMISS_CONTROL, "1,2,3");
- suggestion.intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
-
- // Dismiss suggestion when smart suggestion is not enabled.
- mSuggestionParser.dismissSuggestion(suggestion);
-
- assertThat(mSuggestionParser.isDismissed(suggestion, true /* isSmartSuggestionEnabled */))
- .isTrue();
- }
-
- private void readAndDismissSuggestion(boolean isSmartSuggestionEnabled) {
- mSuggestionsBeforeDismiss = new ArrayList<>();
- mSuggestionsAfterDismiss = new ArrayList<>();
- mSuggestionParser.readSuggestions(
- mMultipleCategory, mSuggestionsBeforeDismiss, isSmartSuggestionEnabled);
-
- final Tile suggestion = mSuggestionsBeforeDismiss.get(0);
- if (mSuggestionParser.dismissSuggestion(suggestion)) {
- mPackageManager.removeResolveInfosForIntent(
- new Intent(Intent.ACTION_MAIN).addCategory(mMultipleCategory.category),
- suggestion.intent.getComponent().getPackageName());
- }
- mSuggestionParser.readSuggestions(
- mMultipleCategory, mSuggestionsAfterDismiss, isSmartSuggestionEnabled);
- }
-}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 4fa0c07..fe9f1b5 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,25 +1,5 @@
# Connectivity / Networking
-per-file ConnectivityService.java=codewiz@google.com
-per-file ConnectivityService.java=ek@google.com
-per-file ConnectivityService.java=jchalard@google.com
-per-file ConnectivityService.java=lorenzo@google.com
-per-file ConnectivityService.java=reminv@google.com
-per-file ConnectivityService.java=satk@google.com
-per-file NetworkManagementService.java=codewiz@google.com
-per-file NetworkManagementService.java=ek@google.com
-per-file NetworkManagementService.java=jchalard@google.com
-per-file NetworkManagementService.java=lorenzo@google.com
-per-file NetworkManagementService.java=reminv@google.com
-per-file NetworkManagementService.java=satk@google.com
-per-file NsdService.java=codewiz@google.com
-per-file NsdService.java=ek@google.com
-per-file NsdService.java=jchalard@google.com
-per-file NsdService.java=lorenzo@google.com
-per-file NsdService.java=reminv@google.com
-per-file NsdService.java=satk@google.com
+per-file ConnectivityService.java,NetworkManagementService.java,NsdService.java = codewiz@google.com, ek@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
-# Vibrator
-per-file VibratorService.java=michaelwr@google.com
-
-# Threads
-per-file DisplayThread.java=michaelwr@google.com
+# Vibrator / Threads
+per-file VibratorService.java, DisplayThread.java = michaelwr@google.com
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index ad9fa40..566ce4f 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -36,6 +36,7 @@
import android.telephony.CellLocation;
import android.telephony.DisconnectCause;
import android.telephony.LocationAccessPolicy;
+import android.telephony.PhoneCapability;
import android.telephony.PhoneStateListener;
import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
@@ -47,7 +48,6 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.VoLteServiceState;
-import android.text.TextUtils;
import android.util.LocalLog;
import com.android.internal.app.IBatteryStats;
@@ -200,6 +200,8 @@
private boolean mCarrierNetworkChangeState = false;
+ private PhoneCapability mPhoneCapability = null;
+
private final LocalLog mLocalLog = new LocalLog(100);
private PreciseDataConnectionState mPreciseDataConnectionState =
@@ -658,6 +660,13 @@
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) {
+ try {
+ r.callback.onPhoneCapabilityChanged(mPhoneCapability);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
}
}
} else {
@@ -1453,6 +1462,33 @@
}
}
+ public void notifyPhoneCapabilityChanged(PhoneCapability capability) {
+ if (!checkNotifyPermission("notifyPhoneCapabilityChanged()")) {
+ return;
+ }
+
+ if (VDBG) {
+ log("notifyPhoneCapabilityChanged: capability=" + capability);
+ }
+
+ synchronized (mRecords) {
+ mPhoneCapability = capability;
+
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE)) {
+ try {
+ r.callback.onPhoneCapabilityChanged(capability);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -1488,6 +1524,7 @@
pw.println("mForegroundCallState=" + mForegroundCallState);
pw.println("mBackgroundCallState=" + mBackgroundCallState);
pw.println("mVoLteServiceState=" + mVoLteServiceState);
+ pw.println("mPhoneCapability=" + mPhoneCapability);
pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 9eb9ab9..96bf44a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -546,6 +546,9 @@
case "-e":
listEnabled = true;
break;
+ case "-a":
+ getFlags |= PackageManager.MATCH_KNOWN_PACKAGES;
+ break;
case "-f":
showSourceDir = true;
break;
@@ -2687,6 +2690,7 @@
pw.println(" Prints all packages; optionally only those whose name contains");
pw.println(" the text in FILTER. Options are:");
pw.println(" -f: see their associated file");
+ pw.println(" -a: all known packages");
pw.println(" -d: filter to only show disabled packages");
pw.println(" -e: filter to only show enabled packages");
pw.println(" -s: filter to only show system packages");
diff --git a/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
index 91e6bd6..11f2b61 100644
--- a/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
@@ -24,10 +24,17 @@
*/
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 @@
*/
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 0000000..d2a15b3
--- /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 0000000..7e57c9f
--- /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 6458232..ef9af52 100644
--- a/services/net/java/android/net/dhcp/DhcpNakPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpNakPacket.java
@@ -26,11 +26,9 @@
/**
* 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 @@
*/
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 d90a4a2..888821a 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 @@
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 @@
* 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 @@
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 @@
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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
/**
* 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 @@
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 @@
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 @@
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 @@
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 @@
}
newPacket.mBroadcastAddress = bcAddr;
+ newPacket.mClientId = clientId;
newPacket.mDnsServers = dnsServers;
newPacket.mDomainName = domainName;
newPacket.mGateways = gateways;
@@ -1173,8 +1214,8 @@
*/
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 @@
/**
* 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 @@
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 0000000..498fd93
--- /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 0000000..3958303
--- /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 4f9aa01..231d0457 100644
--- a/services/net/java/android/net/dhcp/DhcpRequestPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpRequestPacket.java
@@ -28,9 +28,9 @@
/**
* 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 0000000..6d58bc6
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpServingParams.java
@@ -0,0 +1,312 @@
+/*
+ * 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 com.google.android.collect.Sets;
+
+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 default routers to be advertised to DHCP clients.
+ *
+ * <p>Each router must be inside the served prefix. This may be an empty list of routers,
+ * but it must always be set explicitly before building the {@link DhcpServingParams}.
+ */
+ public Builder setDefaultRouters(@NonNull Inet4Address... defaultRouters) {
+ return setDefaultRouters(Sets.newArraySet(defaultRouters));
+ }
+
+ /**
+ * Convenience method to build the parameters with no default router.
+ *
+ * <p>Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address.
+ */
+ public Builder withNoDefaultRouter() {
+ return setDefaultRouters();
+ }
+
+ /**
+ * 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 the DNS servers to be advertised to DHCP clients.
+ *
+ * <p>This may be an empty list of servers, but it must always be set explicitly before
+ * building the {@link DhcpServingParams}.
+ */
+ public Builder setDnsServers(@NonNull Inet4Address... dnsServers) {
+ return setDnsServers(Sets.newArraySet(dnsServers));
+ }
+
+ /**
+ * Convenience method to build the parameters with no DNS server.
+ *
+ * <p>Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address.
+ */
+ public Builder withNoDnsServer() {
+ return setDnsServers();
+ }
+
+ /**
+ * 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 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 Inet4Address... excludedAddrs) {
+ return setExcludedAddrs(Sets.newArraySet(excludedAddrs));
+ }
+
+ /**
+ * 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 7f821ff..b77da28 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -524,7 +524,7 @@
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 0000000..575444f
--- /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 de04fd0..3defe56 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -77,10 +77,12 @@
/**
* 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 10da2a5..4aec6b6 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)];
+ protected PacketReader(Handler h, int recvBufSize) {
+ super(h, new byte[max(recvBufSize, DEFAULT_RECV_BUF_SIZE)]);
}
- public final void start() {
- if (onCorrectThread()) {
- createAndRegisterFd();
- } else {
- mHandler.post(() -> {
- logError("start() called from off-thread", null);
- createAndRegisterFd();
- });
- }
+ @Override
+ protected final int recvBufSize(byte[] buffer) {
+ return buffer.length;
}
- public final void stop() {
- if (onCorrectThread()) {
- unregisterAndDestroyFd();
- } else {
- mHandler.post(() -> {
- logError("stop() called from off-thread", null);
- unregisterAndDestroyFd();
- });
- }
- }
-
- 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 bbd3d13..f7bf393 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 @@
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/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index d25e59f..9a4ea9e7 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* Represents a distinct method to place or receive a phone call. Apps which can place calls and
@@ -360,6 +361,33 @@
private boolean mIsEnabled;
private String mGroupId;
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PhoneAccount that = (PhoneAccount) o;
+ return mCapabilities == that.mCapabilities &&
+ mHighlightColor == that.mHighlightColor &&
+ mSupportedAudioRoutes == that.mSupportedAudioRoutes &&
+ mIsEnabled == that.mIsEnabled &&
+ Objects.equals(mAccountHandle, that.mAccountHandle) &&
+ Objects.equals(mAddress, that.mAddress) &&
+ Objects.equals(mSubscriptionAddress, that.mSubscriptionAddress) &&
+ Objects.equals(mLabel, that.mLabel) &&
+ Objects.equals(mShortDescription, that.mShortDescription) &&
+ Objects.equals(mSupportedUriSchemes, that.mSupportedUriSchemes) &&
+ areBundlesEqual(mExtras, that.mExtras) &&
+ Objects.equals(mGroupId, that.mGroupId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAccountHandle, mAddress, mSubscriptionAddress, mCapabilities,
+ mHighlightColor, mLabel, mShortDescription, mSupportedUriSchemes,
+ mSupportedAudioRoutes,
+ mExtras, mIsEnabled, mGroupId);
+ }
+
/**
* Helper class for creating a {@link PhoneAccount}.
*/
@@ -1022,4 +1050,31 @@
return sb.toString();
}
+
+ /**
+ * Determines if two {@link Bundle}s are equal.
+ * @param extras First {@link Bundle} to check.
+ * @param newExtras {@link Bundle} to compare against.
+ * @return {@code true} if the {@link Bundle}s are equal, {@code false} otherwise.
+ */
+ private static boolean areBundlesEqual(Bundle extras, Bundle newExtras) {
+ if (extras == null || newExtras == null) {
+ return extras == newExtras;
+ }
+
+ if (extras.size() != newExtras.size()) {
+ return false;
+ }
+
+ for(String key : extras.keySet()) {
+ if (key != null) {
+ final Object value = extras.get(key);
+ final Object newValue = newExtras.get(key);
+ if (!Objects.equals(value, newValue)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fdee2f4..d37ef1a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1970,18 +1970,25 @@
* the binding intent go to.
* @hide
*/
- public static final String KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING
- = "carrier_network_service_wlan_package_override_string";
+ public static final String KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING =
+ "carrier_network_service_wlan_package_override_string";
/**
* Decides when clients try to bind to wwan (cellular) network service, which package name will
* the binding intent go to.
* @hide
*/
- public static final String KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING
- = "carrier_network_service_wwan_package_override_string";
+ public static final String KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING =
+ "carrier_network_service_wwan_package_override_string";
/**
+ * The package name of qualified networks service that telephony binds to.
+ *
+ * @hide
+ */
+ public static final String KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING =
+ "carrier_qualified_networks_service_package_override_string";
+ /**
* A list of 4 LTE RSCP thresholds above which a signal level is considered POOR,
* MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
*
@@ -2061,6 +2068,7 @@
sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, "");
+ sDefaults.putString(KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING, "");
sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, "");
sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, "");
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index c240dbb..76a0026 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -129,6 +129,12 @@
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 5b67dc4..9218bdc 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -19,7 +19,7 @@
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 @@
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 5c84791..cb9dbf3 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -19,6 +19,7 @@
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 @@
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 65c904b..b44e891 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -19,6 +19,7 @@
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 @@
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 21b9601..5a9e474 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 @@
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 e26fcb3..727d990 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -19,6 +19,7 @@
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 @@
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/telephony/java/android/telephony/ModemInfo.java b/telephony/java/android/telephony/ModemInfo.java
new file mode 100644
index 0000000..564effe
--- /dev/null
+++ b/telephony/java/android/telephony/ModemInfo.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Information of a single logical modem indicating
+ * its id, supported rats and whether it supports voice or data, etc.
+ * @hide
+ */
+public class ModemInfo implements Parcelable {
+ public final int modemId;
+ public final int rat; /* bitset */
+ public final boolean isVoiceSupported;
+ public final boolean isDataSupported;
+
+ public ModemInfo(int modemId, int rat, boolean isVoiceSupported, boolean isDataSupported) {
+ this.modemId = modemId;
+ this.rat = rat;
+ this.isVoiceSupported = isVoiceSupported;
+ this.isDataSupported = isDataSupported;
+ }
+
+ public ModemInfo(Parcel in) {
+ modemId = in.readInt();
+ rat = in.readInt();
+ isVoiceSupported = in.readBoolean();
+ isDataSupported = in.readBoolean();
+ }
+
+ @Override
+ public String toString() {
+ return "modemId=" + modemId + " rat=" + rat + " isVoiceSupported:" + isVoiceSupported
+ + " isDataSupported:" + isDataSupported;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(modemId, rat, isVoiceSupported, isDataSupported);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof ModemInfo) || hashCode() != o.hashCode()) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ ModemInfo s = (ModemInfo) o;
+
+ return (modemId == s.modemId
+ && rat == s.rat
+ && isVoiceSupported == s.isVoiceSupported
+ && isDataSupported == s.isDataSupported);
+ }
+
+ /**
+ * {@link Parcelable#describeContents}
+ */
+ public @ContentsFlags int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@link Parcelable#writeToParcel}
+ */
+ public void writeToParcel(Parcel dest, @WriteFlags int flags) {
+ dest.writeInt(modemId);
+ dest.writeInt(rat);
+ dest.writeBoolean(isVoiceSupported);
+ dest.writeBoolean(isDataSupported);
+ }
+
+ public static final Parcelable.Creator<ModemInfo> CREATOR = new Parcelable.Creator() {
+ public ModemInfo createFromParcel(Parcel in) {
+ return new ModemInfo(in);
+ }
+
+ public ModemInfo[] newArray(int size) {
+ return new ModemInfo[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/PhoneCapability.aidl b/telephony/java/android/telephony/PhoneCapability.aidl
new file mode 100644
index 0000000..5de8d4a
--- /dev/null
+++ b/telephony/java/android/telephony/PhoneCapability.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.telephony;
+
+parcelable PhoneCapability;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java
new file mode 100644
index 0000000..2ebfa53
--- /dev/null
+++ b/telephony/java/android/telephony/PhoneCapability.java
@@ -0,0 +1,111 @@
+/*
+ * 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.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Define capability of a modem group. That is, the capabilities
+ * are shared between those modems defined by list of modem IDs.
+ * @hide
+ */
+public class PhoneCapability implements Parcelable {
+ public final int maxActiveVoiceCalls;
+ public final int maxActiveData;
+ public final int max5G;
+ public final List<ModemInfo> logicalModemList;
+
+ public PhoneCapability(int maxActiveVoiceCalls, int maxActiveData, int max5G,
+ List<ModemInfo> logicalModemList) {
+ this.maxActiveVoiceCalls = maxActiveVoiceCalls;
+ this.maxActiveData = maxActiveData;
+ this.max5G = max5G;
+ // Make sure it's not null.
+ this.logicalModemList = logicalModemList == null ? new ArrayList<>() : logicalModemList;
+ }
+
+ @Override
+ public String toString() {
+ return "maxActiveVoiceCalls=" + maxActiveVoiceCalls + " maxActiveData=" + maxActiveData
+ + " max5G=" + max5G + "logicalModemList:"
+ + Arrays.toString(logicalModemList.toArray());
+ }
+
+ private PhoneCapability(Parcel in) {
+ maxActiveVoiceCalls = in.readInt();
+ maxActiveData = in.readInt();
+ max5G = in.readInt();
+ logicalModemList = new ArrayList<>();
+ in.readList(logicalModemList, ModemInfo.class.getClassLoader());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof PhoneCapability) || hashCode() != o.hashCode()) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ PhoneCapability s = (PhoneCapability) o;
+
+ return (maxActiveVoiceCalls == s.maxActiveVoiceCalls
+ && maxActiveData == s.maxActiveData
+ && max5G == s.max5G
+ && logicalModemList.equals(s.logicalModemList));
+ }
+
+ /**
+ * {@link Parcelable#describeContents}
+ */
+ public @Parcelable.ContentsFlags int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@link Parcelable#writeToParcel}
+ */
+ public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
+ dest.writeInt(maxActiveVoiceCalls);
+ dest.writeInt(maxActiveData);
+ dest.writeInt(max5G);
+ dest.writeList(logicalModemList);
+ }
+
+ public static final Parcelable.Creator<PhoneCapability> CREATOR = new Parcelable.Creator() {
+ public PhoneCapability createFromParcel(Parcel in) {
+ return new PhoneCapability(in);
+ }
+
+ public PhoneCapability[] newArray(int size) {
+ return new PhoneCapability[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 7f7ce8e..bd6a59d 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -22,12 +22,11 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.telecom.TelecomManager;
import com.android.internal.telephony.IPhoneStateListener;
-import java.util.List;
import java.lang.ref.WeakReference;
+import java.util.List;
/**
* A listener class for monitoring changes in specific telephony states
@@ -273,6 +272,14 @@
*/
public static final int LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 0x00100000;
+ /**
+ * Listen for changes to the phone capability.
+ *
+ * @see #onPhoneCapabilityChanged
+ * @hide
+ */
+ public static final int LISTEN_PHONE_CAPABILITY_CHANGE = 0x00200000;
+
/*
* Subscription used to listen to the phone state changes
* @hide
@@ -395,6 +402,10 @@
PhoneStateListener.this.onPhysicalChannelConfigurationChanged(
(List<PhysicalChannelConfig>)msg.obj);
break;
+ case LISTEN_PHONE_CAPABILITY_CHANGE:
+ PhoneStateListener.this.onPhoneCapabilityChanged(
+ (PhoneCapability) msg.obj);
+ break;
}
}
};
@@ -625,6 +636,16 @@
}
/**
+ * Callback invoked when phone capability changes. Requires
+ * the READ_PRIVILEGED_PHONE_STATE permission.
+ * @param capability the new phone capability
+ * @hide
+ */
+ public void onPhoneCapabilityChanged(PhoneCapability capability) {
+ // default implementation empty
+ }
+
+ /**
* Callback invoked when telephony has received notice from a carrier
* app that a network action that could result in connectivity loss
* has been requested by an app using
@@ -751,6 +772,10 @@
public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
send(LISTEN_PHYSICAL_CHANNEL_CONFIGURATION, 0, 0, configs);
}
+
+ public void onPhoneCapabilityChanged(PhoneCapability capability) {
+ send(LISTEN_PHONE_CAPABILITY_CHANGE, 0, 0, capability);
+ }
}
@UnsupportedAppUsage
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2e0bc6c..cc841fa 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8125,4 +8125,23 @@
}
return UNKNOWN_CARRIER_ID_LIST_VERSION;
}
+
+
+ /**
+ * How many modems can have simultaneous data connections.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getNumberOfModemsWithSimultaneousDataConnections() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getNumberOfModemsWithSimultaneousDataConnections(
+ getSubId(), mContext.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ }
+ return 0;
+ }
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 9388ed1..0e4a7ad 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -810,7 +810,7 @@
version = 1;
}
- String[] a = data.split("\\s*,\\s*");
+ String[] a = data.split("\\s*,\\s*", -1);
if (a.length < 14) {
return null;
}
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 32cb8ce..c976666 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -183,6 +183,15 @@
public static final int CODE_EMERGENCY_PERM_FAILURE = 364;
/**
+ * Call failure code during hangup/reject if user marked the call as unwanted.
+ *
+ * Android Telephony will receive information whether ROBO call feature is supported by the
+ * network from modem and propagate the same to AOSP as new ImsCallProfile members. OEMs can
+ * check this information and provide an option to the user to mark the call as unwanted.
+ */
+ public static final int CODE_SIP_USER_MARKED_UNWANTED = 365;
+
+ /**
* MEDIA (IMS -> Telephony)
*/
// Media resource initialization failed
@@ -401,6 +410,12 @@
public static final int CODE_UNOBTAINABLE_NUMBER = 1515;
/**
+ * Call failed because WiFi call could not complete and circuit switch silent redial
+ * is not allowed while roaming on another network.
+ */
+ public static final int CODE_NO_CSFB_IN_CS_ROAM = 1516;
+
+ /**
* The rejection cause is not known.
* <p>
* Used with implicit call rejection.
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 0d315e5..1ebb697 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -21,6 +21,7 @@
import android.telephony.SignalStrength;
import android.telephony.CellInfo;
import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.PhoneCapability;
import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
@@ -50,5 +51,6 @@
void onOemHookRawEvent(in byte[] rawData);
void onCarrierNetworkChange(in boolean active);
void onUserMobileDataStateChanged(in boolean enabled);
+ void onPhoneCapabilityChanged(in PhoneCapability capability);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 5cf3dff..7c6dbca 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1537,4 +1537,10 @@
* @hide
*/
void refreshUiccProfile(int subId);
+
+ /**
+ * How many modems can have simultaneous data connections.
+ * @hide
+ */
+ int getNumberOfModemsWithSimultaneousDataConnections(int subId, String callingPackage);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 0127db9..e0e1a7b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -21,6 +21,7 @@
import android.net.NetworkCapabilities;
import android.os.Bundle;
import android.telephony.CellInfo;
+import android.telephony.PhoneCapability;
import android.telephony.PhysicalChannelConfig;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -74,4 +75,5 @@
void notifySubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
+ void notifyPhoneCapabilityChanged(in PhoneCapability capability);
}
diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
index 2b172da..3452819 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.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 @@
assertInvalidNetworkMask(IPv4Address("255.255.0.255"));
}
-
@Test
public void testPrefixLengthToV4NetmaskIntHTL() {
assertEquals(0, prefixLengthToV4NetmaskIntHTL(0));
@@ -266,4 +267,44 @@
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 0000000..edadd6e
--- /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 0000000..b6a4073
--- /dev/null
+++ b/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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.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(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(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/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 8e7e5e5..6bb762a 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -23,6 +23,7 @@
#include "Debug.h"
#include "Diagnostics.h"
#include "Flags.h"
+#include "LoadedApk.h"
#include "format/Container.h"
#include "format/binary/BinaryResourceParser.h"
#include "format/proto/ProtoDeserialize.h"
@@ -254,6 +255,32 @@
return true;
}
+static bool DumpPackageName(IAaptContext* context, const std::string& file_path) {
+ auto loaded_apk = LoadedApk::LoadApkFromPath(file_path, context->GetDiagnostics());
+ if (!loaded_apk) {
+ return false;
+ }
+
+ constexpr size_t kStdOutBufferSize = 1024u;
+ io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
+ Printer printer(&fout);
+
+ xml::Element* manifest_el = loaded_apk->GetManifest()->root.get();
+ if (!manifest_el) {
+ context->GetDiagnostics()->Error(DiagMessage() << "No AndroidManifest.");
+ return false;
+ }
+
+ xml::Attribute* attr = manifest_el->FindAttribute({}, "package");
+ if (!attr) {
+ context->GetDiagnostics()->Error(DiagMessage() << "No package name.");
+ return false;
+ }
+ printer.Println(StringPrintf("%s", attr->value.c_str()));
+
+ return true;
+}
+
namespace {
class DumpContext : public IAaptContext {
@@ -324,9 +351,20 @@
DumpContext context;
context.SetVerbose(verbose);
+ auto parsedArgs = flags.GetArgs();
+ if (parsedArgs.size() > 1 && parsedArgs[0] == "packagename") {
+ parsedArgs.erase(parsedArgs.begin());
+ for (const std::string& arg : parsedArgs) {
+ if (!DumpPackageName(&context, arg)) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
options.print_options.show_sources = true;
options.print_options.show_values = !no_values;
- for (const std::string& arg : flags.GetArgs()) {
+ for (const std::string& arg : parsedArgs) {
if (!TryDumpFile(&context, arg, options)) {
return 1;
}