Merge "Add white edgelights to AOSP" into qt-dev
diff --git a/api/test-current.txt b/api/test-current.txt
index ee6fb51..1a912a1c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1917,6 +1917,10 @@
method public boolean hasSingleFileDescriptor();
}
+ public final class Parcel {
+ method public int readExceptionCode();
+ }
+
public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable {
method public static java.io.File getFile(java.io.FileDescriptor) throws java.io.IOException;
}
@@ -1986,6 +1990,7 @@
public class SystemProperties {
method @NonNull public static String get(@NonNull String);
method @NonNull public static String get(@NonNull String, @Nullable String);
+ method public static boolean getBoolean(@NonNull String, boolean);
}
public final class UserHandle implements android.os.Parcelable {
@@ -2140,6 +2145,36 @@
}
+package android.os.image {
+
+ public class DynamicSystemClient {
+ ctor public DynamicSystemClient(@NonNull android.content.Context);
+ method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void bind();
+ method public void setOnStatusChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.image.DynamicSystemClient.OnStatusChangedListener);
+ method public void setOnStatusChangedListener(@NonNull android.os.image.DynamicSystemClient.OnStatusChangedListener);
+ method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void start(@NonNull android.net.Uri, long);
+ method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void start(@NonNull android.net.Uri, long, long);
+ method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void unbind();
+ field public static final int CAUSE_ERROR_EXCEPTION = 6; // 0x6
+ field public static final int CAUSE_ERROR_INVALID_URL = 4; // 0x4
+ field public static final int CAUSE_ERROR_IO = 3; // 0x3
+ field public static final int CAUSE_ERROR_IPC = 5; // 0x5
+ field public static final int CAUSE_INSTALL_CANCELLED = 2; // 0x2
+ field public static final int CAUSE_INSTALL_COMPLETED = 1; // 0x1
+ field public static final int CAUSE_NOT_SPECIFIED = 0; // 0x0
+ field public static final int STATUS_IN_PROGRESS = 2; // 0x2
+ field public static final int STATUS_IN_USE = 4; // 0x4
+ field public static final int STATUS_NOT_STARTED = 1; // 0x1
+ field public static final int STATUS_READY = 3; // 0x3
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ public static interface DynamicSystemClient.OnStatusChangedListener {
+ method public void onStatusChanged(int, int, long, @Nullable Throwable);
+ }
+
+}
+
package android.os.storage {
public class StorageManager {
@@ -2946,6 +2981,21 @@
method public E valueAtUnchecked(int);
}
+ public class FeatureFlagUtils {
+ ctor public FeatureFlagUtils();
+ method public static java.util.Map<java.lang.String,java.lang.String> getAllFeatureFlags();
+ method public static boolean isEnabled(android.content.Context, String);
+ method public static void setEnabled(android.content.Context, String, boolean);
+ field public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
+ field public static final String FFLAG_OVERRIDE_PREFIX = "sys.fflag.override.";
+ field public static final String FFLAG_PREFIX = "sys.fflag.";
+ field public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
+ field public static final String PERSIST_PREFIX = "persist.sys.fflag.override.";
+ field public static final String PIXEL_WALLPAPER_CATEGORY_SWITCH = "settings_pixel_wallpaper_category_switch";
+ field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
+ field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
+ }
+
public class TimeUtils {
method public static String formatDuration(long);
}
diff --git a/config/hiddenapi-greylist-packages.txt b/config/hiddenapi-greylist-packages.txt
index cae3bd9..986d259 100644
--- a/config/hiddenapi-greylist-packages.txt
+++ b/config/hiddenapi-greylist-packages.txt
@@ -1,2 +1,43 @@
+gov.nist.core
+gov.nist.core.net
+gov.nist.javax.sip
+gov.nist.javax.sip.address
+gov.nist.javax.sip.clientauthutils
+gov.nist.javax.sip.header
+gov.nist.javax.sip.header.extensions
+gov.nist.javax.sip.header.ims
+gov.nist.javax.sip.message
+gov.nist.javax.sip.parser
+gov.nist.javax.sip.parser.extensions
+gov.nist.javax.sip.parser.ims
+gov.nist.javax.sip.stack
+org.apache.xalan
+org.apache.xalan.extensions
+org.apache.xalan.processor
+org.apache.xalan.res
+org.apache.xalan.serialize
+org.apache.xalan.templates
+org.apache.xalan.transformer
+org.apache.xalan.xslt
+org.apache.xml.dtm
+org.apache.xml.dtm.ref
+org.apache.xml.dtm.ref.dom2dtm
+org.apache.xml.dtm.ref.sax2dtm
+org.apache.xml.res
+org.apache.xml.serializer
+org.apache.xml.serializer.dom3
+org.apache.xml.serializer.utils
+org.apache.xml.utils
+org.apache.xml.utils.res
+org.apache.xpath
+org.apache.xpath.axes
+org.apache.xpath.compiler
+org.apache.xpath.domapi
+org.apache.xpath.functions
+org.apache.xpath.jaxp
+org.apache.xpath.objects
+org.apache.xpath.operations
+org.apache.xpath.patterns
+org.apache.xpath.res
org.ccil.cowan.tagsoup
org.ccil.cowan.tagsoup.jaxp
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 82d0fbe..10f25ea 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1774,715 +1774,3 @@
Lcom/google/android/mms/util/SqliteWrapper;->requery(Landroid/content/Context;Landroid/database/Cursor;)Z
Lcom/google/android/mms/util/SqliteWrapper;->update(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I
Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type;
-Lgov/nist/core/Debug;->printStackTrace(Ljava/lang/Exception;)V
-Lgov/nist/core/GenericObject;-><init>()V
-Lgov/nist/core/GenericObject;->dbgPrint()V
-Lgov/nist/core/GenericObject;->debugDump(I)Ljava/lang/String;
-Lgov/nist/core/GenericObject;->encode()Ljava/lang/String;
-Lgov/nist/core/GenericObject;->getMatcher()Lgov/nist/core/Match;
-Lgov/nist/core/GenericObject;->indentation:I
-Lgov/nist/core/GenericObject;->isMySubclass(Ljava/lang/Class;)Z
-Lgov/nist/core/GenericObject;->match(Ljava/lang/Object;)Z
-Lgov/nist/core/GenericObject;->matchExpression:Lgov/nist/core/Match;
-Lgov/nist/core/GenericObject;->merge(Ljava/lang/Object;)V
-Lgov/nist/core/GenericObject;->sprint(Ljava/lang/String;)V
-Lgov/nist/core/GenericObject;->stringRepresentation:Ljava/lang/String;
-Lgov/nist/core/GenericObjectList;-><init>()V
-Lgov/nist/core/GenericObjectList;-><init>(Ljava/lang/String;)V
-Lgov/nist/core/GenericObjectList;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
-Lgov/nist/core/GenericObjectList;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Lgov/nist/core/GenericObjectList;->concatenate(Lgov/nist/core/GenericObjectList;)V
-Lgov/nist/core/GenericObjectList;->concatenate(Lgov/nist/core/GenericObjectList;Z)V
-Lgov/nist/core/GenericObjectList;->debugDump(I)Ljava/lang/String;
-Lgov/nist/core/GenericObjectList;->first()Lgov/nist/core/GenericObject;
-Lgov/nist/core/GenericObjectList;->getIndentation()Ljava/lang/String;
-Lgov/nist/core/GenericObjectList;->indentation:I
-Lgov/nist/core/GenericObjectList;->isMySubclass(Ljava/lang/Class;)Z
-Lgov/nist/core/GenericObjectList;->match(Ljava/lang/Object;)Z
-Lgov/nist/core/GenericObjectList;->myClass:Ljava/lang/Class;
-Lgov/nist/core/GenericObjectList;->next()Lgov/nist/core/GenericObject;
-Lgov/nist/core/GenericObjectList;->next(Ljava/util/ListIterator;)Lgov/nist/core/GenericObject;
-Lgov/nist/core/GenericObjectList;->setMyClass(Ljava/lang/Class;)V
-Lgov/nist/core/GenericObjectList;->stringRep:Ljava/lang/String;
-Lgov/nist/core/Host;-><init>()V
-Lgov/nist/core/Host;-><init>(Ljava/lang/String;)V
-Lgov/nist/core/Host;->encode()Ljava/lang/String;
-Lgov/nist/core/Host;->getAddress()Ljava/lang/String;
-Lgov/nist/core/Host;->getHostname()Ljava/lang/String;
-Lgov/nist/core/Host;->isIPv6Reference(Ljava/lang/String;)Z
-Lgov/nist/core/Host;->setAddress(Ljava/lang/String;)V
-Lgov/nist/core/Host;->setHostname(Ljava/lang/String;)V
-Lgov/nist/core/HostNameParser;-><init>(Lgov/nist/core/LexerCore;)V
-Lgov/nist/core/HostNameParser;-><init>(Ljava/lang/String;)V
-Lgov/nist/core/HostNameParser;->host()Lgov/nist/core/Host;
-Lgov/nist/core/HostNameParser;->hostPort(Z)Lgov/nist/core/HostPort;
-Lgov/nist/core/HostPort;-><init>()V
-Lgov/nist/core/HostPort;->encode()Ljava/lang/String;
-Lgov/nist/core/HostPort;->encode(Ljava/lang/StringBuffer;)Ljava/lang/StringBuffer;
-Lgov/nist/core/HostPort;->getHost()Lgov/nist/core/Host;
-Lgov/nist/core/HostPort;->getInetAddress()Ljava/net/InetAddress;
-Lgov/nist/core/HostPort;->getPort()I
-Lgov/nist/core/HostPort;->hasPort()Z
-Lgov/nist/core/HostPort;->removePort()V
-Lgov/nist/core/HostPort;->setHost(Lgov/nist/core/Host;)V
-Lgov/nist/core/HostPort;->setPort(I)V
-Lgov/nist/core/InternalErrorHandler;->handleException(Ljava/lang/Exception;)V
-Lgov/nist/core/InternalErrorHandler;->handleException(Ljava/lang/String;)V
-Lgov/nist/core/LexerCore;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Lgov/nist/core/LexerCore;->byteStringNoSemicolon()Ljava/lang/String;
-Lgov/nist/core/LexerCore;->byteStringNoSlash()Ljava/lang/String;
-Lgov/nist/core/LexerCore;->charAsString(I)Ljava/lang/String;
-Lgov/nist/core/LexerCore;->comment()Ljava/lang/String;
-Lgov/nist/core/LexerCore;->createParseException()Ljava/text/ParseException;
-Lgov/nist/core/LexerCore;->currentLexer:Ljava/util/Hashtable;
-Lgov/nist/core/LexerCore;->getBuffer()Ljava/lang/String;
-Lgov/nist/core/LexerCore;->getNextId()Ljava/lang/String;
-Lgov/nist/core/LexerCore;->getNextToken()Lgov/nist/core/Token;
-Lgov/nist/core/LexerCore;->getPtr()I
-Lgov/nist/core/LexerCore;->getRest()Ljava/lang/String;
-Lgov/nist/core/LexerCore;->getString(C)Ljava/lang/String;
-Lgov/nist/core/LexerCore;->isTokenChar(C)Z
-Lgov/nist/core/LexerCore;->lexerTables:Ljava/util/Hashtable;
-Lgov/nist/core/LexerCore;->markInputPosition()I
-Lgov/nist/core/LexerCore;->match(I)Lgov/nist/core/Token;
-Lgov/nist/core/LexerCore;->number()Ljava/lang/String;
-Lgov/nist/core/LexerCore;->peekNextToken()Lgov/nist/core/Token;
-Lgov/nist/core/LexerCore;->peekNextToken(I)[Lgov/nist/core/Token;
-Lgov/nist/core/LexerCore;->quotedString()Ljava/lang/String;
-Lgov/nist/core/LexerCore;->rewindInputPosition(I)V
-Lgov/nist/core/LexerCore;->selectLexer(Ljava/lang/String;)V
-Lgov/nist/core/LexerCore;->SPorHT()V
-Lgov/nist/core/LexerCore;->startsId()Z
-Lgov/nist/core/LexerCore;->ttoken()Ljava/lang/String;
-Lgov/nist/core/LexerCore;->ttokenSafe()Ljava/lang/String;
-Lgov/nist/core/Match;->match(Ljava/lang/String;)Z
-Lgov/nist/core/NameValue;-><init>()V
-Lgov/nist/core/NameValue;-><init>(Ljava/lang/String;Ljava/lang/Object;)V
-Lgov/nist/core/NameValue;-><init>(Ljava/lang/String;Ljava/lang/Object;Z)V
-Lgov/nist/core/NameValue;->encode()Ljava/lang/String;
-Lgov/nist/core/NameValue;->encode(Ljava/lang/StringBuffer;)Ljava/lang/StringBuffer;
-Lgov/nist/core/NameValue;->getName()Ljava/lang/String;
-Lgov/nist/core/NameValue;->getValueAsObject()Ljava/lang/Object;
-Lgov/nist/core/NameValue;->setName(Ljava/lang/String;)V
-Lgov/nist/core/NameValue;->setQuotedValue()V
-Lgov/nist/core/NameValue;->setSeparator(Ljava/lang/String;)V
-Lgov/nist/core/NameValue;->setValueAsObject(Ljava/lang/Object;)V
-Lgov/nist/core/NameValueList;-><init>()V
-Lgov/nist/core/NameValueList;-><init>(Z)V
-Lgov/nist/core/NameValueList;->delete(Ljava/lang/String;)Z
-Lgov/nist/core/NameValueList;->encode()Ljava/lang/String;
-Lgov/nist/core/NameValueList;->encode(Ljava/lang/StringBuffer;)Ljava/lang/StringBuffer;
-Lgov/nist/core/NameValueList;->getNames()Ljava/util/Iterator;
-Lgov/nist/core/NameValueList;->getNameValue(Ljava/lang/String;)Lgov/nist/core/NameValue;
-Lgov/nist/core/NameValueList;->getParameter(Ljava/lang/String;)Ljava/lang/String;
-Lgov/nist/core/NameValueList;->getValue(Ljava/lang/String;)Ljava/lang/Object;
-Lgov/nist/core/NameValueList;->hasNameValue(Ljava/lang/String;)Z
-Lgov/nist/core/NameValueList;->iterator()Ljava/util/Iterator;
-Lgov/nist/core/NameValueList;->set(Lgov/nist/core/NameValue;)V
-Lgov/nist/core/NameValueList;->set(Ljava/lang/String;Ljava/lang/Object;)V
-Lgov/nist/core/NameValueList;->setSeparator(Ljava/lang/String;)V
-Lgov/nist/core/net/DefaultNetworkLayer;->SINGLETON:Lgov/nist/core/net/DefaultNetworkLayer;
-Lgov/nist/core/net/NetworkLayer;->createDatagramSocket()Ljava/net/DatagramSocket;
-Lgov/nist/core/net/NetworkLayer;->createDatagramSocket(ILjava/net/InetAddress;)Ljava/net/DatagramSocket;
-Lgov/nist/core/net/NetworkLayer;->createServerSocket(IILjava/net/InetAddress;)Ljava/net/ServerSocket;
-Lgov/nist/core/net/NetworkLayer;->createSocket(Ljava/net/InetAddress;I)Ljava/net/Socket;
-Lgov/nist/core/net/NetworkLayer;->createSSLServerSocket(IILjava/net/InetAddress;)Ljavax/net/ssl/SSLServerSocket;
-Lgov/nist/core/net/NetworkLayer;->createSSLSocket(Ljava/net/InetAddress;I)Ljavax/net/ssl/SSLSocket;
-Lgov/nist/core/ParserCore;-><init>()V
-Lgov/nist/core/ParserCore;->lexer:Lgov/nist/core/LexerCore;
-Lgov/nist/core/StringTokenizer;->ptr:I
-Lgov/nist/core/ThreadAuditor$ThreadHandle;->getPingIntervalInMillisecs()J
-Lgov/nist/core/ThreadAuditor$ThreadHandle;->ping()V
-Lgov/nist/core/ThreadAuditor;-><init>()V
-Lgov/nist/core/ThreadAuditor;->addCurrentThread()Lgov/nist/core/ThreadAuditor$ThreadHandle;
-Lgov/nist/core/ThreadAuditor;->getPingIntervalInMillisecs()J
-Lgov/nist/core/ThreadAuditor;->isEnabled()Z
-Lgov/nist/core/ThreadAuditor;->setPingIntervalInMillisecs(J)V
-Lgov/nist/core/Token;-><init>()V
-Lgov/nist/core/Token;->getTokenType()I
-Lgov/nist/core/Token;->getTokenValue()Ljava/lang/String;
-Lgov/nist/javax/sip/address/GenericURI;-><init>()V
-Lgov/nist/javax/sip/address/GenericURI;->encode()Ljava/lang/String;
-Lgov/nist/javax/sip/address/GenericURI;->getScheme()Ljava/lang/String;
-Lgov/nist/javax/sip/address/SipUri;->getHost()Ljava/lang/String;
-Lgov/nist/javax/sip/address/SipUri;->getParameter(Ljava/lang/String;)Ljava/lang/String;
-Lgov/nist/javax/sip/address/SipUri;->getPort()I
-Lgov/nist/javax/sip/address/SipUri;->getUser()Ljava/lang/String;
-Lgov/nist/javax/sip/address/SipUri;->removeParameter(Ljava/lang/String;)V
-Lgov/nist/javax/sip/address/SipUri;->setParameter(Ljava/lang/String;Ljava/lang/String;)V
-Lgov/nist/javax/sip/address/SipUri;->setUserParam(Ljava/lang/String;)V
-Lgov/nist/javax/sip/parser/URLParser;-><init>(Ljava/lang/String;)V
-Lgov/nist/javax/sip/parser/URLParser;->sipURL(Z)Lgov/nist/javax/sip/address/SipUri;
-Lorg/apache/xalan/extensions/ExpressionContext;->getContextNode()Lorg/w3c/dom/Node;
-Lorg/apache/xalan/extensions/ExpressionContext;->getErrorListener()Ljavax/xml/transform/ErrorListener;
-Lorg/apache/xalan/extensions/ExpressionContext;->getVariableOrParam(Lorg/apache/xml/utils/QName;)Lorg/apache/xpath/objects/XObject;
-Lorg/apache/xalan/extensions/ExpressionContext;->getXPathContext()Lorg/apache/xpath/XPathContext;
-Lorg/apache/xalan/extensions/ExtensionHandler;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Lorg/apache/xalan/extensions/ExtensionHandler;->callFunction(Ljava/lang/String;Ljava/util/Vector;Ljava/lang/Object;Lorg/apache/xalan/extensions/ExpressionContext;)Ljava/lang/Object;
-Lorg/apache/xalan/extensions/ExtensionHandler;->getClassForName(Ljava/lang/String;)Ljava/lang/Class;
-Lorg/apache/xalan/extensions/ObjectFactory$ConfigurationError;-><init>(Ljava/lang/String;Ljava/lang/Exception;)V
-Lorg/apache/xalan/extensions/ObjectFactory;->findClassLoader()Ljava/lang/ClassLoader;
-Lorg/apache/xalan/extensions/ObjectFactory;->findProviderClass(Ljava/lang/String;Ljava/lang/ClassLoader;Z)Ljava/lang/Class;
-Lorg/apache/xalan/processor/TransformerFactoryImpl;-><init>()V
-Lorg/apache/xalan/res/XSLMessages;->createMessage(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
-Lorg/apache/xalan/res/XSLTErrorResources;-><init>()V
-Lorg/apache/xalan/serialize/SerializerUtils;->outputResultTreeFragment(Lorg/apache/xml/serializer/SerializationHandler;Lorg/apache/xpath/objects/XObject;Lorg/apache/xpath/XPathContext;)V
-Lorg/apache/xalan/templates/AVT;->evaluate(Lorg/apache/xpath/XPathContext;ILorg/apache/xml/utils/PrefixResolver;)Ljava/lang/String;
-Lorg/apache/xalan/templates/ElemElement;->execute(Lorg/apache/xalan/transformer/TransformerImpl;)V
-Lorg/apache/xalan/templates/ElemExsltFunction;->execute(Lorg/apache/xalan/transformer/TransformerImpl;[Lorg/apache/xpath/objects/XObject;)V
-Lorg/apache/xalan/templates/ElemExtensionCall;->getAttribute(Ljava/lang/String;Lorg/w3c/dom/Node;Lorg/apache/xalan/transformer/TransformerImpl;)Ljava/lang/String;
-Lorg/apache/xalan/templates/ElemLiteralResult;->getLiteralResultAttribute(Ljava/lang/String;)Lorg/apache/xalan/templates/AVT;
-Lorg/apache/xalan/templates/ElemTemplate;->getMatch()Lorg/apache/xpath/XPath;
-Lorg/apache/xalan/templates/ElemTemplate;->getName()Lorg/apache/xml/utils/QName;
-Lorg/apache/xalan/templates/ElemTemplateElement;->getFirstChildElem()Lorg/apache/xalan/templates/ElemTemplateElement;
-Lorg/apache/xalan/templates/ElemTemplateElement;->getNextSiblingElem()Lorg/apache/xalan/templates/ElemTemplateElement;
-Lorg/apache/xalan/templates/ElemTemplateElement;->getParentElem()Lorg/apache/xalan/templates/ElemTemplateElement;
-Lorg/apache/xalan/templates/ElemTemplateElement;->getStylesheetRoot()Lorg/apache/xalan/templates/StylesheetRoot;
-Lorg/apache/xalan/templates/ElemTemplateElement;->getXSLToken()I
-Lorg/apache/xalan/templates/ElemTextLiteral;->getChars()[C
-Lorg/apache/xalan/templates/KeyDeclaration;->getName()Lorg/apache/xml/utils/QName;
-Lorg/apache/xalan/templates/KeyDeclaration;->getUse()Lorg/apache/xpath/XPath;
-Lorg/apache/xalan/templates/StylesheetRoot;->getDefaultRootRule()Lorg/apache/xalan/templates/ElemTemplate;
-Lorg/apache/xalan/templates/StylesheetRoot;->getDefaultRule()Lorg/apache/xalan/templates/ElemTemplate;
-Lorg/apache/xalan/templates/StylesheetRoot;->getDefaultTextRule()Lorg/apache/xalan/templates/ElemTemplate;
-Lorg/apache/xalan/templates/StylesheetRoot;->getTemplateComposed(Lorg/apache/xml/utils/QName;)Lorg/apache/xalan/templates/ElemTemplate;
-Lorg/apache/xalan/transformer/ClonerToResultTree;->cloneToResultTree(IILorg/apache/xml/dtm/DTM;Lorg/apache/xml/serializer/SerializationHandler;Z)V
-Lorg/apache/xalan/transformer/DecimalToRoman;-><init>(JLjava/lang/String;JLjava/lang/String;)V
-Lorg/apache/xalan/transformer/DecimalToRoman;->m_postLetter:Ljava/lang/String;
-Lorg/apache/xalan/transformer/DecimalToRoman;->m_postValue:J
-Lorg/apache/xalan/transformer/DecimalToRoman;->m_preLetter:Ljava/lang/String;
-Lorg/apache/xalan/transformer/DecimalToRoman;->m_preValue:J
-Lorg/apache/xalan/transformer/MsgMgr;->error(Ljavax/xml/transform/SourceLocator;Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;Ljava/lang/String;)V
-Lorg/apache/xalan/transformer/TransformerImpl;->createSerializationHandler(Ljavax/xml/transform/Result;Lorg/apache/xalan/templates/OutputProperties;)Lorg/apache/xml/serializer/SerializationHandler;
-Lorg/apache/xalan/transformer/TransformerImpl;->executeChildTemplates(Lorg/apache/xalan/templates/ElemTemplateElement;Lorg/w3c/dom/Node;Lorg/apache/xml/utils/QName;Lorg/xml/sax/ContentHandler;)V
-Lorg/apache/xalan/transformer/TransformerImpl;->executeChildTemplates(Lorg/apache/xalan/templates/ElemTemplateElement;Z)V
-Lorg/apache/xalan/transformer/TransformerImpl;->getCountersTable()Lorg/apache/xalan/transformer/CountersTable;
-Lorg/apache/xalan/transformer/TransformerImpl;->getCurrentTemplateElements()Lorg/apache/xml/utils/ObjectStack;
-Lorg/apache/xalan/transformer/TransformerImpl;->getCurrentTemplateElementsCount()I
-Lorg/apache/xalan/transformer/TransformerImpl;->getMatchedNode()I
-Lorg/apache/xalan/transformer/TransformerImpl;->getMatchedTemplate()Lorg/apache/xalan/templates/ElemTemplate;
-Lorg/apache/xalan/transformer/TransformerImpl;->getMode()Lorg/apache/xml/utils/QName;
-Lorg/apache/xalan/transformer/TransformerImpl;->getMsgMgr()Lorg/apache/xalan/transformer/MsgMgr;
-Lorg/apache/xalan/transformer/TransformerImpl;->getOutputFormat()Lorg/apache/xalan/templates/OutputProperties;
-Lorg/apache/xalan/transformer/TransformerImpl;->getResultTreeHandler()Lorg/apache/xml/serializer/SerializationHandler;
-Lorg/apache/xalan/transformer/TransformerImpl;->getSerializationHandler()Lorg/apache/xml/serializer/SerializationHandler;
-Lorg/apache/xalan/transformer/TransformerImpl;->getXPathContext()Lorg/apache/xpath/XPathContext;
-Lorg/apache/xalan/transformer/TransformerImpl;->m_attrSetStack:Ljava/util/Stack;
-Lorg/apache/xalan/transformer/TransformerImpl;->m_currentMatchedNodes:Lorg/apache/xml/utils/NodeVector;
-Lorg/apache/xalan/transformer/TransformerImpl;->m_currentMatchTemplates:Ljava/util/Stack;
-Lorg/apache/xalan/transformer/TransformerImpl;->m_currentTemplateElements:Lorg/apache/xml/utils/ObjectStack;
-Lorg/apache/xalan/transformer/TransformerImpl;->m_currentTemplateRuleIsNull:Lorg/apache/xml/utils/BoolStack;
-Lorg/apache/xalan/transformer/TransformerImpl;->m_inputContentHandler:Lorg/xml/sax/ContentHandler;
-Lorg/apache/xalan/transformer/TransformerImpl;->m_outputTarget:Ljavax/xml/transform/Result;
-Lorg/apache/xalan/transformer/TransformerImpl;->m_stringWriterObjectPool:Lorg/apache/xml/utils/ObjectPool;
-Lorg/apache/xalan/transformer/TransformerImpl;->m_urlOfSource:Ljava/lang/String;
-Lorg/apache/xalan/transformer/TransformerImpl;->m_xcontext:Lorg/apache/xpath/XPathContext;
-Lorg/apache/xalan/transformer/TransformerImpl;->popCurrentFuncResult()Ljava/lang/Object;
-Lorg/apache/xalan/transformer/TransformerImpl;->pushCurrentFuncResult(Ljava/lang/Object;)V
-Lorg/apache/xalan/transformer/TransformerImpl;->pushElemTemplateElement(Lorg/apache/xalan/templates/ElemTemplateElement;)V
-Lorg/apache/xalan/Version;->getVersion()Ljava/lang/String;
-Lorg/apache/xalan/xslt/EnvironmentCheck;-><init>()V
-Lorg/apache/xalan/xslt/EnvironmentCheck;->appendEnvironmentReport(Lorg/w3c/dom/Node;Lorg/w3c/dom/Document;Ljava/util/Hashtable;)V
-Lorg/apache/xalan/xslt/EnvironmentCheck;->getEnvironmentHash()Ljava/util/Hashtable;
-Lorg/apache/xalan/xslt/ObjectFactory;->findClassLoader()Ljava/lang/ClassLoader;
-Lorg/apache/xalan/xslt/ObjectFactory;->newInstance(Ljava/lang/String;Ljava/lang/ClassLoader;Z)Ljava/lang/Object;
-Lorg/apache/xml/dtm/Axis;->getNames(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/Axis;->isReverse(I)Z
-Lorg/apache/xml/dtm/DTM;->getDocument()I
-Lorg/apache/xml/dtm/DTM;->getDocumentRoot(I)I
-Lorg/apache/xml/dtm/DTM;->getFirstChild(I)I
-Lorg/apache/xml/dtm/DTM;->getNextSibling(I)I
-Lorg/apache/xml/dtm/DTM;->getNode(I)Lorg/w3c/dom/Node;
-Lorg/apache/xml/dtm/DTM;->getNodeName(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/DTM;->getNodeType(I)S
-Lorg/apache/xml/dtm/DTM;->getParent(I)I
-Lorg/apache/xml/dtm/DTM;->getSourceLocatorFor(I)Ljavax/xml/transform/SourceLocator;
-Lorg/apache/xml/dtm/DTM;->getStringValue(I)Lorg/apache/xml/utils/XMLString;
-Lorg/apache/xml/dtm/DTM;->migrateTo(Lorg/apache/xml/dtm/DTMManager;)V
-Lorg/apache/xml/dtm/DTMAxisIterator;->cloneIterator()Lorg/apache/xml/dtm/DTMAxisIterator;
-Lorg/apache/xml/dtm/DTMAxisIterator;->getLast()I
-Lorg/apache/xml/dtm/DTMAxisIterator;->getNodeByPosition(I)I
-Lorg/apache/xml/dtm/DTMAxisIterator;->getPosition()I
-Lorg/apache/xml/dtm/DTMAxisIterator;->gotoMark()V
-Lorg/apache/xml/dtm/DTMAxisIterator;->isReverse()Z
-Lorg/apache/xml/dtm/DTMAxisIterator;->next()I
-Lorg/apache/xml/dtm/DTMAxisIterator;->reset()Lorg/apache/xml/dtm/DTMAxisIterator;
-Lorg/apache/xml/dtm/DTMAxisIterator;->setMark()V
-Lorg/apache/xml/dtm/DTMAxisIterator;->setRestartable(Z)V
-Lorg/apache/xml/dtm/DTMAxisIterator;->setStartNode(I)Lorg/apache/xml/dtm/DTMAxisIterator;
-Lorg/apache/xml/dtm/DTMException;-><init>(Ljava/lang/String;)V
-Lorg/apache/xml/dtm/DTMFilter;->acceptNode(II)S
-Lorg/apache/xml/dtm/DTMIterator;->cloneWithReset()Lorg/apache/xml/dtm/DTMIterator;
-Lorg/apache/xml/dtm/DTMIterator;->getCurrentPos()I
-Lorg/apache/xml/dtm/DTMIterator;->getDTM(I)Lorg/apache/xml/dtm/DTM;
-Lorg/apache/xml/dtm/DTMIterator;->nextNode()I
-Lorg/apache/xml/dtm/DTMIterator;->runTo(I)V
-Lorg/apache/xml/dtm/DTMIterator;->setCurrentPos(I)V
-Lorg/apache/xml/dtm/DTMIterator;->setRoot(ILjava/lang/Object;)V
-Lorg/apache/xml/dtm/DTMIterator;->setShouldCacheNodes(Z)V
-Lorg/apache/xml/dtm/DTMManager;->getDTM(Ljavax/xml/transform/Source;ZLorg/apache/xml/dtm/DTMWSFilter;ZZ)Lorg/apache/xml/dtm/DTM;
-Lorg/apache/xml/dtm/DTMManager;->getXMLStringFactory()Lorg/apache/xml/utils/XMLStringFactory;
-Lorg/apache/xml/dtm/DTMManager;->release(Lorg/apache/xml/dtm/DTM;Z)Z
-Lorg/apache/xml/dtm/ref/CoroutineManager;-><init>()V
-Lorg/apache/xml/dtm/ref/CoroutineManager;->co_joinCoroutineSet(I)I
-Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;-><init>()V
-Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->includeSelf()Lorg/apache/xml/dtm/DTMAxisIterator;
-Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->reset()Lorg/apache/xml/dtm/DTMAxisIterator;
-Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->resetPosition()Lorg/apache/xml/dtm/DTMAxisIterator;
-Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->returnNode(I)I
-Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->setRestartable(Z)V
-Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->_includeSelf:Z
-Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->_isRestartable:Z
-Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->_last:I
-Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->_markedNode:I
-Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->_position:I
-Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->_startNode:I
-Lorg/apache/xml/dtm/ref/DTMAxisIterNodeList;-><init>(Lorg/apache/xml/dtm/DTM;Lorg/apache/xml/dtm/DTMAxisIterator;)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->appendChild(IZZ)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->appendTextChild(Ljava/lang/String;)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->declareNamespaceInContext(II)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->documentRegistration()V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->documentRelease()V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->ensureSizeOfIndex(II)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->error(Ljava/lang/String;)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->findGTE([IIII)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->findInSortedSuballocatedIntVector(Lorg/apache/xml/utils/SuballocatedIntVector;I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->findNamespaceContext(I)Lorg/apache/xml/utils/SuballocatedIntVector;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocument()I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentAllDeclarationsProcessed()Z
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentBaseURI()Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentEncoding(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentRoot(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentStandalone(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentSystemIdentifier(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentVersion(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDTMIDs()Lorg/apache/xml/utils/SuballocatedIntVector;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getExpandedTypeID(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getExpandedTypeID(Ljava/lang/String;Ljava/lang/String;I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getFirstChild(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getFirstNamespaceNode(IZ)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getLastChild(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getLevel(I)S
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getLocalNameFromExpandedNameID(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getManager()Lorg/apache/xml/dtm/DTMManager;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNamespaceFromExpandedNameID(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNamespaceType(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNextAttribute(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNextNamespaceNode(IIZ)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNextSibling(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNode(I)Lorg/w3c/dom/Node;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNodeHandle(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNodeIdent(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNodeType(I)S
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getOwnerDocument(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getParent(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getPreviousSibling(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getShouldStripWhitespace()Z
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getStringValueChunk(II[I)[C
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getStringValueChunkCount(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->hasChildNodes(I)Z
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->indexNode(II)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->isCharacterElementContentWhitespace(I)Z
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->isDocumentAllDeclarationsProcessed(I)Z
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->isNodeAfter(II)Z
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->isSupported(Ljava/lang/String;Ljava/lang/String;)Z
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->makeNodeHandle(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->makeNodeIdentity(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_expandedNameTable:Lorg/apache/xml/dtm/ref/ExpandedNameTable;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_exptype:Lorg/apache/xml/utils/SuballocatedIntVector;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_firstch:Lorg/apache/xml/utils/SuballocatedIntVector;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_nextsib:Lorg/apache/xml/utils/SuballocatedIntVector;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_parent:Lorg/apache/xml/utils/SuballocatedIntVector;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_prevsib:Lorg/apache/xml/utils/SuballocatedIntVector;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_size:I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_wsfilter:Lorg/apache/xml/dtm/DTMWSFilter;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_xstrf:Lorg/apache/xml/utils/XMLStringFactory;
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->popShouldStripWhitespace()V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->pushShouldStripWhitespace(Z)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->setDocumentBaseURI(Ljava/lang/String;)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->setFeature(Ljava/lang/String;Z)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->setShouldStripWhitespace(Z)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->supportsPreStripping()Z
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_exptype(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_firstch(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_level(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_nextsib(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_parent(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_prevsib(I)I
-Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_type(I)S
-Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$InternalAxisIteratorBase;-><init>(Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$InternalAxisIteratorBase;->_currentNode:I
-Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$NamespaceIterator;-><init>(Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$NamespaceIterator;->next()I
-Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$NamespaceIterator;->setStartNode(I)Lorg/apache/xml/dtm/DTMAxisIterator;
-Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$NthDescendantIterator;-><init>(Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;I)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$SingletonIterator;-><init>(Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$SingletonIterator;-><init>(Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;I)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;-><init>(Lorg/apache/xml/dtm/DTMManager;Ljavax/xml/transform/Source;ILorg/apache/xml/dtm/DTMWSFilter;Lorg/apache/xml/utils/XMLStringFactory;Z)V
-Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;->getAxisIterator(I)Lorg/apache/xml/dtm/DTMAxisIterator;
-Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;->getTypedAxisIterator(II)Lorg/apache/xml/dtm/DTMAxisIterator;
-Lorg/apache/xml/dtm/ref/DTMDefaultBaseTraversers;->getAxisTraverser(I)Lorg/apache/xml/dtm/DTMAxisTraverser;
-Lorg/apache/xml/dtm/ref/DTMManagerDefault;-><init>()V
-Lorg/apache/xml/dtm/ref/DTMManagerDefault;->addDTM(Lorg/apache/xml/dtm/DTM;I)V
-Lorg/apache/xml/dtm/ref/DTMManagerDefault;->addDTM(Lorg/apache/xml/dtm/DTM;II)V
-Lorg/apache/xml/dtm/ref/DTMManagerDefault;->getFirstFreeDTMID()I
-Lorg/apache/xml/dtm/ref/DTMManagerDefault;->getXMLReader(Ljavax/xml/transform/Source;)Lorg/xml/sax/XMLReader;
-Lorg/apache/xml/dtm/ref/DTMManagerDefault;->releaseXMLReader(Lorg/xml/sax/XMLReader;)V
-Lorg/apache/xml/dtm/ref/DTMNodeIterator;-><init>(Lorg/apache/xml/dtm/DTMIterator;)V
-Lorg/apache/xml/dtm/ref/DTMNodeIterator;->getDTMIterator()Lorg/apache/xml/dtm/DTMIterator;
-Lorg/apache/xml/dtm/ref/DTMNodeIterator;->getRoot()Lorg/w3c/dom/Node;
-Lorg/apache/xml/dtm/ref/DTMNodeList;-><init>(Lorg/apache/xml/dtm/DTMIterator;)V
-Lorg/apache/xml/dtm/ref/DTMNodeProxy;-><init>(Lorg/apache/xml/dtm/DTM;I)V
-Lorg/apache/xml/dtm/ref/DTMNodeProxy;->getDTM()Lorg/apache/xml/dtm/DTM;
-Lorg/apache/xml/dtm/ref/DTMNodeProxy;->getDTMNodeNumber()I
-Lorg/apache/xml/dtm/ref/DTMNodeProxy;->getStringValue()Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/DTMStringPool;-><init>()V
-Lorg/apache/xml/dtm/ref/DTMStringPool;->indexToString(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/DTMStringPool;->m_intToString:Ljava/util/Vector;
-Lorg/apache/xml/dtm/ref/DTMStringPool;->removeAllElements()V
-Lorg/apache/xml/dtm/ref/DTMStringPool;->stringToIndex(Ljava/lang/String;)I
-Lorg/apache/xml/dtm/ref/ExpandedNameTable;->getExpandedTypeID(Ljava/lang/String;Ljava/lang/String;I)I
-Lorg/apache/xml/dtm/ref/ExpandedNameTable;->getExpandedTypeID(Ljava/lang/String;Ljava/lang/String;IZ)I
-Lorg/apache/xml/dtm/ref/ExpandedNameTable;->getLocalName(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/ExpandedNameTable;->getSize()I
-Lorg/apache/xml/dtm/ref/ExpandedNameTable;->getType(I)S
-Lorg/apache/xml/dtm/ref/IncrementalSAXSource;->deliverMoreNodes(Z)Ljava/lang/Object;
-Lorg/apache/xml/dtm/ref/IncrementalSAXSource;->setContentHandler(Lorg/xml/sax/ContentHandler;)V
-Lorg/apache/xml/dtm/ref/IncrementalSAXSource;->setLexicalHandler(Lorg/xml/sax/ext/LexicalHandler;)V
-Lorg/apache/xml/dtm/ref/IncrementalSAXSource;->startParse(Lorg/xml/sax/InputSource;)V
-Lorg/apache/xml/dtm/ref/IncrementalSAXSource_Filter;-><init>()V
-Lorg/apache/xml/dtm/ref/IncrementalSAXSource_Filter;->setXMLReader(Lorg/xml/sax/XMLReader;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$AncestorIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$AncestorIterator;->next()I
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$AncestorIterator;->setStartNode(I)Lorg/apache/xml/dtm/DTMAxisIterator;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$AttributeIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$ChildrenIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$ChildrenIterator;->setStartNode(I)Lorg/apache/xml/dtm/DTMAxisIterator;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$DescendantIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$FollowingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$FollowingSiblingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$ParentIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$ParentIterator;->setNodeType(I)Lorg/apache/xml/dtm/DTMAxisIterator;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$PrecedingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$PrecedingSiblingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedAncestorIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedAttributeIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedChildrenIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedDescendantIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedFollowingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedFollowingSiblingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedPrecedingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedPrecedingSiblingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedSingletonIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;-><init>(Lorg/apache/xml/dtm/DTMManager;Ljavax/xml/transform/Source;ILorg/apache/xml/dtm/DTMWSFilter;Lorg/apache/xml/utils/XMLStringFactory;ZIZZZ)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->copyAttribute(IILorg/apache/xml/serializer/SerializationHandler;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->copyAttributes(ILorg/apache/xml/serializer/SerializationHandler;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->copyElement(IILorg/apache/xml/serializer/SerializationHandler;)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->copyNS(ILorg/apache/xml/serializer/SerializationHandler;Z)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->copyTextNode(ILorg/apache/xml/serializer/SerializationHandler;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->dispatchCharactersEvents(ILorg/xml/sax/ContentHandler;Z)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getFirstAttribute(I)I
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getIdForNamespace(Ljava/lang/String;)I
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getLocalName(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getNodeName(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getNodeNameX(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getNodeValue(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getStringValue()Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getStringValue(I)Lorg/apache/xml/utils/XMLString;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getStringValueX(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->m_buildIdIndex:Z
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->_exptype2(I)I
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->_exptype2Type(I)I
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->_firstch2(I)I
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->_nextsib2(I)I
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->dispatchToEvents(ILorg/xml/sax/ContentHandler;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getAttributeNode(ILjava/lang/String;Ljava/lang/String;)I
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getContentHandler()Lorg/xml/sax/ContentHandler;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getDeclHandler()Lorg/xml/sax/ext/DeclHandler;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getDocumentTypeDeclarationPublicIdentifier()Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getDocumentTypeDeclarationSystemIdentifier()Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getDTDHandler()Lorg/xml/sax/DTDHandler;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getEntityResolver()Lorg/xml/sax/EntityResolver;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getErrorHandler()Lorg/xml/sax/ErrorHandler;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getLexicalHandler()Lorg/xml/sax/ext/LexicalHandler;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getNamespaceURI(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getNumberOfNodes()I
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getPrefix(I)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getSourceLocatorFor(I)Ljavax/xml/transform/SourceLocator;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getUnparsedEntityURI(Ljava/lang/String;)Ljava/lang/String;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->isAttributeSpecified(I)Z
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->migrateTo(Lorg/apache/xml/dtm/DTMManager;)V
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->m_idAttributes:Ljava/util/Hashtable;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->m_parents:Lorg/apache/xml/utils/IntStack;
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->m_previous:I
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->needsTwoThreads()Z
-Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->setProperty(Ljava/lang/String;Ljava/lang/Object;)V
-Lorg/apache/xml/dtm/ref/SecuritySupport;->getContextClassLoader()Ljava/lang/ClassLoader;
-Lorg/apache/xml/dtm/ref/SecuritySupport;->getFileExists(Ljava/io/File;)Z
-Lorg/apache/xml/dtm/ref/SecuritySupport;->getFileInputStream(Ljava/io/File;)Ljava/io/FileInputStream;
-Lorg/apache/xml/dtm/ref/SecuritySupport;->getInstance()Lorg/apache/xml/dtm/ref/SecuritySupport;
-Lorg/apache/xml/dtm/ref/SecuritySupport;->getLastModified(Ljava/io/File;)J
-Lorg/apache/xml/dtm/ref/SecuritySupport;->getParentClassLoader(Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;
-Lorg/apache/xml/dtm/ref/SecuritySupport;->getResourceAsStream(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/io/InputStream;
-Lorg/apache/xml/dtm/ref/SecuritySupport;->getSystemClassLoader()Ljava/lang/ClassLoader;
-Lorg/apache/xml/dtm/ref/SecuritySupport;->getSystemProperty(Ljava/lang/String;)Ljava/lang/String;
-Lorg/apache/xml/res/XMLErrorResources;-><init>()V
-Lorg/apache/xml/res/XMLMessages;->createXMLMessage(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
-Lorg/apache/xml/serializer/CharInfo$CharKey;-><init>(C)V
-Lorg/apache/xml/serializer/CharInfo;-><init>(Ljava/lang/String;Ljava/lang/String;Z)V
-Lorg/apache/xml/serializer/CharInfo;->get(I)Z
-Lorg/apache/xml/serializer/CharInfo;->getCharInfo(Ljava/lang/String;Ljava/lang/String;)Lorg/apache/xml/serializer/CharInfo;
-Lorg/apache/xml/serializer/CharInfo;->set(I)V
-Lorg/apache/xml/serializer/dom3/LSSerializerImpl;-><init>()V
-Lorg/apache/xml/serializer/DOMSerializer;->serialize(Lorg/w3c/dom/Node;)V
-Lorg/apache/xml/serializer/ElemContext;->m_elementName:Ljava/lang/String;
-Lorg/apache/xml/serializer/ElemContext;->m_elementURI:Ljava/lang/String;
-Lorg/apache/xml/serializer/ElemContext;->m_startTagOpen:Z
-Lorg/apache/xml/serializer/ElemContext;->push(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lorg/apache/xml/serializer/ElemContext;
-Lorg/apache/xml/serializer/ElemDesc;->isAttrFlagSet(Ljava/lang/String;I)Z
-Lorg/apache/xml/serializer/Encodings;->convertMime2JavaEncoding(Ljava/lang/String;)Ljava/lang/String;
-Lorg/apache/xml/serializer/Encodings;->getMimeEncoding(Ljava/lang/String;)Ljava/lang/String;
-Lorg/apache/xml/serializer/Encodings;->getWriter(Ljava/io/OutputStream;Ljava/lang/String;)Ljava/io/Writer;
-Lorg/apache/xml/serializer/NamespaceMappings;-><init>()V
-Lorg/apache/xml/serializer/NamespaceMappings;->generateNextPrefix()Ljava/lang/String;
-Lorg/apache/xml/serializer/NamespaceMappings;->lookupNamespace(Ljava/lang/String;)Ljava/lang/String;
-Lorg/apache/xml/serializer/NamespaceMappings;->lookupPrefix(Ljava/lang/String;)Ljava/lang/String;
-Lorg/apache/xml/serializer/OutputPropertiesFactory;->getDefaultMethodProperties(Ljava/lang/String;)Ljava/util/Properties;
-Lorg/apache/xml/serializer/OutputPropertyUtils;->getBooleanProperty(Ljava/lang/String;Ljava/util/Properties;)Z
-Lorg/apache/xml/serializer/OutputPropertyUtils;->getIntProperty(Ljava/lang/String;Ljava/util/Properties;)I
-Lorg/apache/xml/serializer/SerializationHandler;->close()V
-Lorg/apache/xml/serializer/SerializationHandler;->flushPending()V
-Lorg/apache/xml/serializer/SerializationHandler;->setEscaping(Z)Z
-Lorg/apache/xml/serializer/SerializationHandler;->setIndentAmount(I)V
-Lorg/apache/xml/serializer/SerializationHandler;->setNamespaceMappings(Lorg/apache/xml/serializer/NamespaceMappings;)V
-Lorg/apache/xml/serializer/Serializer;->asContentHandler()Lorg/xml/sax/ContentHandler;
-Lorg/apache/xml/serializer/Serializer;->asDOMSerializer()Lorg/apache/xml/serializer/DOMSerializer;
-Lorg/apache/xml/serializer/Serializer;->getOutputFormat()Ljava/util/Properties;
-Lorg/apache/xml/serializer/Serializer;->getOutputStream()Ljava/io/OutputStream;
-Lorg/apache/xml/serializer/Serializer;->getWriter()Ljava/io/Writer;
-Lorg/apache/xml/serializer/Serializer;->reset()Z
-Lorg/apache/xml/serializer/Serializer;->setOutputFormat(Ljava/util/Properties;)V
-Lorg/apache/xml/serializer/Serializer;->setOutputStream(Ljava/io/OutputStream;)V
-Lorg/apache/xml/serializer/Serializer;->setWriter(Ljava/io/Writer;)V
-Lorg/apache/xml/serializer/SerializerBase;->fireCharEvent([CII)V
-Lorg/apache/xml/serializer/SerializerBase;->fireCommentEvent([CII)V
-Lorg/apache/xml/serializer/SerializerBase;->fireEndDoc()V
-Lorg/apache/xml/serializer/SerializerBase;->fireEndElem(Ljava/lang/String;)V
-Lorg/apache/xml/serializer/SerializerBase;->fireEscapingEvent(Ljava/lang/String;Ljava/lang/String;)V
-Lorg/apache/xml/serializer/SerializerBase;->getDoctypePublic()Ljava/lang/String;
-Lorg/apache/xml/serializer/SerializerBase;->getDoctypeSystem()Ljava/lang/String;
-Lorg/apache/xml/serializer/SerializerBase;->getEncoding()Ljava/lang/String;
-Lorg/apache/xml/serializer/SerializerBase;->getPrefixPart(Ljava/lang/String;)Ljava/lang/String;
-Lorg/apache/xml/serializer/SerializerBase;->getVersion()Ljava/lang/String;
-Lorg/apache/xml/serializer/SerializerBase;->m_attributes:Lorg/apache/xml/serializer/AttributesImplSerializer;
-Lorg/apache/xml/serializer/SerializerBase;->m_charsBuff:[C
-Lorg/apache/xml/serializer/SerializerBase;->m_elemContext:Lorg/apache/xml/serializer/ElemContext;
-Lorg/apache/xml/serializer/SerializerBase;->m_needToCallStartDocument:Z
-Lorg/apache/xml/serializer/SerializerBase;->m_tracer:Lorg/apache/xml/serializer/SerializerTrace;
-Lorg/apache/xml/serializer/SerializerBase;->setDoctypePublic(Ljava/lang/String;)V
-Lorg/apache/xml/serializer/SerializerBase;->setDoctypeSystem(Ljava/lang/String;)V
-Lorg/apache/xml/serializer/SerializerBase;->setIndent(Z)V
-Lorg/apache/xml/serializer/SerializerBase;->setMediaType(Ljava/lang/String;)V
-Lorg/apache/xml/serializer/SerializerBase;->setOmitXMLDeclaration(Z)V
-Lorg/apache/xml/serializer/SerializerBase;->setStandalone(Ljava/lang/String;)V
-Lorg/apache/xml/serializer/SerializerBase;->setStandaloneInternal(Ljava/lang/String;)V
-Lorg/apache/xml/serializer/SerializerBase;->setVersion(Ljava/lang/String;)V
-Lorg/apache/xml/serializer/SerializerFactory;->getSerializer(Ljava/util/Properties;)Lorg/apache/xml/serializer/Serializer;
-Lorg/apache/xml/serializer/SerializerTraceWriter;-><init>(Ljava/io/Writer;Lorg/apache/xml/serializer/SerializerTrace;)V
-Lorg/apache/xml/serializer/ToHTMLStream;-><init>()V
-Lorg/apache/xml/serializer/ToHTMLStream;->getElemDesc(Ljava/lang/String;)Lorg/apache/xml/serializer/ElemDesc;
-Lorg/apache/xml/serializer/ToSAXHandler;-><init>(Lorg/xml/sax/ContentHandler;Ljava/lang/String;)V
-Lorg/apache/xml/serializer/ToSAXHandler;-><init>(Lorg/xml/sax/ContentHandler;Lorg/xml/sax/ext/LexicalHandler;Ljava/lang/String;)V
-Lorg/apache/xml/serializer/ToSAXHandler;->m_lexHandler:Lorg/xml/sax/ext/LexicalHandler;
-Lorg/apache/xml/serializer/ToSAXHandler;->m_saxHandler:Lorg/xml/sax/ContentHandler;
-Lorg/apache/xml/serializer/ToSAXHandler;->reset()Z
-Lorg/apache/xml/serializer/ToSAXHandler;->startDocumentInternal()V
-Lorg/apache/xml/serializer/ToSAXHandler;->startElement(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-Lorg/apache/xml/serializer/ToStream;->setCdataSectionElements(Ljava/lang/String;Ljava/util/Properties;)V
-Lorg/apache/xml/serializer/ToStream;->setEncoding(Ljava/lang/String;)V
-Lorg/apache/xml/serializer/ToStream;->setIndentAmount(I)V
-Lorg/apache/xml/serializer/ToTextSAXHandler;-><init>(Lorg/xml/sax/ContentHandler;Ljava/lang/String;)V
-Lorg/apache/xml/serializer/ToTextSAXHandler;-><init>(Lorg/xml/sax/ContentHandler;Lorg/xml/sax/ext/LexicalHandler;Ljava/lang/String;)V
-Lorg/apache/xml/serializer/ToTextStream;-><init>()V
-Lorg/apache/xml/serializer/ToUnknownStream;-><init>()V
-Lorg/apache/xml/serializer/ToXMLSAXHandler;-><init>(Lorg/xml/sax/ContentHandler;Ljava/lang/String;)V
-Lorg/apache/xml/serializer/ToXMLSAXHandler;-><init>(Lorg/xml/sax/ContentHandler;Lorg/xml/sax/ext/LexicalHandler;Ljava/lang/String;)V
-Lorg/apache/xml/serializer/ToXMLStream;-><init>()V
-Lorg/apache/xml/serializer/WriterToASCI;-><init>(Ljava/io/OutputStream;)V
-Lorg/apache/xml/serializer/WriterToUTF8Buffered;-><init>(Ljava/io/OutputStream;)V
-Lorg/apache/xml/utils/DefaultErrorHandler;-><init>()V
-Lorg/apache/xml/utils/DefaultErrorHandler;->printLocation(Ljava/io/PrintWriter;Ljava/lang/Throwable;)V
-Lorg/apache/xml/utils/DOMHelper;->isNodeAfter(Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)Z
-Lorg/apache/xml/utils/DOMHelper;->isNodeTheSame(Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)Z
-Lorg/apache/xml/utils/FastStringBuffer;->append(Ljava/lang/String;)V
-Lorg/apache/xml/utils/FastStringBuffer;->getString(II)Ljava/lang/String;
-Lorg/apache/xml/utils/FastStringBuffer;->length()I
-Lorg/apache/xml/utils/IntStack;->peek()I
-Lorg/apache/xml/utils/ObjectVector;->elementAt(I)Ljava/lang/Object;
-Lorg/apache/xml/utils/ObjectVector;->size()I
-Lorg/apache/xml/utils/PrefixResolverDefault;-><init>(Lorg/w3c/dom/Node;)V
-Lorg/apache/xml/utils/PrefixResolverDefault;->getNamespaceForPrefix(Ljava/lang/String;)Ljava/lang/String;
-Lorg/apache/xml/utils/QName;-><init>(Ljava/lang/String;)V
-Lorg/apache/xml/utils/QName;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Lorg/apache/xml/utils/QName;->getLocalName()Ljava/lang/String;
-Lorg/apache/xml/utils/SAXSourceLocator;-><init>(Lorg/xml/sax/SAXParseException;)V
-Lorg/apache/xml/utils/StringBufferPool;->free(Lorg/apache/xml/utils/FastStringBuffer;)V
-Lorg/apache/xml/utils/StringBufferPool;->get()Lorg/apache/xml/utils/FastStringBuffer;
-Lorg/apache/xml/utils/StringVector;->elementAt(I)Ljava/lang/String;
-Lorg/apache/xml/utils/StringVector;->size()I
-Lorg/apache/xml/utils/StylesheetPIHandler;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-Lorg/apache/xml/utils/StylesheetPIHandler;->getAssociatedStylesheet()Ljavax/xml/transform/Source;
-Lorg/apache/xml/utils/StylesheetPIHandler;->setBaseId(Ljava/lang/String;)V
-Lorg/apache/xml/utils/StylesheetPIHandler;->setURIResolver(Ljavax/xml/transform/URIResolver;)V
-Lorg/apache/xml/utils/SuballocatedIntVector;-><init>(I)V
-Lorg/apache/xml/utils/SuballocatedIntVector;->elementAt(I)I
-Lorg/apache/xml/utils/SuballocatedIntVector;->setElementAt(II)V
-Lorg/apache/xml/utils/SuballocatedIntVector;->size()I
-Lorg/apache/xml/utils/SystemIDResolver;->getAbsoluteURI(Ljava/lang/String;)Ljava/lang/String;
-Lorg/apache/xml/utils/SystemIDResolver;->getAbsoluteURI(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
-Lorg/apache/xml/utils/SystemIDResolver;->getAbsoluteURIFromRelative(Ljava/lang/String;)Ljava/lang/String;
-Lorg/apache/xml/utils/SystemIDResolver;->isAbsoluteURI(Ljava/lang/String;)Z
-Lorg/apache/xml/utils/URI$MalformedURIException;-><init>(Ljava/lang/String;)V
-Lorg/apache/xml/utils/URI;-><init>(Ljava/lang/String;)V
-Lorg/apache/xml/utils/URI;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-Lorg/apache/xml/utils/URI;-><init>(Lorg/apache/xml/utils/URI;)V
-Lorg/apache/xml/utils/URI;-><init>(Lorg/apache/xml/utils/URI;Ljava/lang/String;)V
-Lorg/apache/xml/utils/URI;->getFragment()Ljava/lang/String;
-Lorg/apache/xml/utils/URI;->getHost()Ljava/lang/String;
-Lorg/apache/xml/utils/URI;->getPath()Ljava/lang/String;
-Lorg/apache/xml/utils/URI;->getPort()I
-Lorg/apache/xml/utils/URI;->getQueryString()Ljava/lang/String;
-Lorg/apache/xml/utils/URI;->getScheme()Ljava/lang/String;
-Lorg/apache/xml/utils/URI;->getUserinfo()Ljava/lang/String;
-Lorg/apache/xml/utils/URI;->setFragment(Ljava/lang/String;)V
-Lorg/apache/xml/utils/URI;->setHost(Ljava/lang/String;)V
-Lorg/apache/xml/utils/URI;->setPort(I)V
-Lorg/apache/xml/utils/URI;->setScheme(Ljava/lang/String;)V
-Lorg/apache/xml/utils/URI;->setUserinfo(Ljava/lang/String;)V
-Lorg/apache/xml/utils/WrappedRuntimeException;-><init>(Ljava/lang/Exception;)V
-Lorg/apache/xml/utils/WrappedRuntimeException;->getException()Ljava/lang/Exception;
-Lorg/apache/xml/utils/XML11Char;->isXML11ValidNCName(Ljava/lang/String;)Z
-Lorg/apache/xml/utils/XML11Char;->isXML11ValidQName(Ljava/lang/String;)Z
-Lorg/apache/xml/utils/XMLReaderManager;->getInstance()Lorg/apache/xml/utils/XMLReaderManager;
-Lorg/apache/xml/utils/XMLReaderManager;->getXMLReader()Lorg/xml/sax/XMLReader;
-Lorg/apache/xml/utils/XMLReaderManager;->releaseXMLReader(Lorg/xml/sax/XMLReader;)V
-Lorg/apache/xml/utils/XMLString;->dispatchCharactersEvents(Lorg/xml/sax/ContentHandler;)V
-Lorg/apache/xml/utils/XMLString;->equals(Lorg/apache/xml/utils/XMLString;)Z
-Lorg/apache/xml/utils/XMLString;->fixWhiteSpace(ZZZ)Lorg/apache/xml/utils/XMLString;
-Lorg/apache/xml/utils/XMLStringDefault;-><init>(Ljava/lang/String;)V
-Lorg/apache/xml/utils/XMLStringFactory;-><init>()V
-Lorg/apache/xml/utils/XMLStringFactory;->emptystr()Lorg/apache/xml/utils/XMLString;
-Lorg/apache/xml/utils/XMLStringFactory;->newstr(Ljava/lang/String;)Lorg/apache/xml/utils/XMLString;
-Lorg/apache/xpath/axes/ChildTestIterator;-><init>(Lorg/apache/xml/dtm/DTMAxisTraverser;)V
-Lorg/apache/xpath/axes/DescendantIterator;-><init>()V
-Lorg/apache/xpath/axes/LocPathIterator;->getDTM(I)Lorg/apache/xml/dtm/DTM;
-Lorg/apache/xpath/axes/LocPathIterator;->getPrefixResolver()Lorg/apache/xml/utils/PrefixResolver;
-Lorg/apache/xpath/axes/LocPathIterator;->getXPathContext()Lorg/apache/xpath/XPathContext;
-Lorg/apache/xpath/axes/NodeSequence;->getContainedIter()Lorg/apache/xml/dtm/DTMIterator;
-Lorg/apache/xpath/axes/NodeSequence;->nextNode()I
-Lorg/apache/xpath/axes/OneStepIterator;-><init>(Lorg/apache/xml/dtm/DTMAxisIterator;I)V
-Lorg/apache/xpath/CachedXPathAPI;-><init>()V
-Lorg/apache/xpath/CachedXPathAPI;-><init>(Lorg/apache/xpath/CachedXPathAPI;)V
-Lorg/apache/xpath/CachedXPathAPI;->eval(Lorg/w3c/dom/Node;Ljava/lang/String;)Lorg/apache/xpath/objects/XObject;
-Lorg/apache/xpath/CachedXPathAPI;->getXPathContext()Lorg/apache/xpath/XPathContext;
-Lorg/apache/xpath/CachedXPathAPI;->selectNodeList(Lorg/w3c/dom/Node;Ljava/lang/String;)Lorg/w3c/dom/NodeList;
-Lorg/apache/xpath/CachedXPathAPI;->selectNodeList(Lorg/w3c/dom/Node;Ljava/lang/String;Lorg/w3c/dom/Node;)Lorg/w3c/dom/NodeList;
-Lorg/apache/xpath/CachedXPathAPI;->selectSingleNode(Lorg/w3c/dom/Node;Ljava/lang/String;)Lorg/w3c/dom/Node;
-Lorg/apache/xpath/CachedXPathAPI;->selectSingleNode(Lorg/w3c/dom/Node;Ljava/lang/String;Lorg/w3c/dom/Node;)Lorg/w3c/dom/Node;
-Lorg/apache/xpath/compiler/FunctionTable;-><init>()V
-Lorg/apache/xpath/compiler/FunctionTable;->installFunction(Ljava/lang/String;Ljava/lang/Class;)I
-Lorg/apache/xpath/Expression;->assertion(ZLjava/lang/String;)V
-Lorg/apache/xpath/Expression;->error(Lorg/apache/xpath/XPathContext;Ljava/lang/String;[Ljava/lang/Object;)V
-Lorg/apache/xpath/Expression;->exprGetParent()Lorg/apache/xpath/ExpressionNode;
-Lorg/apache/xpath/ExpressionNode;->exprGetParent()Lorg/apache/xpath/ExpressionNode;
-Lorg/apache/xpath/functions/FuncCurrent;-><init>()V
-Lorg/apache/xpath/functions/FuncExtFunction;->getFunctionName()Ljava/lang/String;
-Lorg/apache/xpath/functions/FuncExtFunction;->getMethodKey()Ljava/lang/Object;
-Lorg/apache/xpath/functions/Function;-><init>()V
-Lorg/apache/xpath/functions/WrongNumberArgsException;-><init>(Ljava/lang/String;)V
-Lorg/apache/xpath/NodeSet;-><init>()V
-Lorg/apache/xpath/NodeSet;-><init>(Lorg/w3c/dom/Node;)V
-Lorg/apache/xpath/NodeSet;-><init>(Lorg/w3c/dom/NodeList;)V
-Lorg/apache/xpath/NodeSet;-><init>(Lorg/w3c/dom/traversal/NodeIterator;)V
-Lorg/apache/xpath/NodeSet;->addElement(Lorg/w3c/dom/Node;)V
-Lorg/apache/xpath/NodeSet;->addNode(Lorg/w3c/dom/Node;)V
-Lorg/apache/xpath/NodeSet;->contains(Lorg/w3c/dom/Node;)Z
-Lorg/apache/xpath/NodeSet;->elementAt(I)Lorg/w3c/dom/Node;
-Lorg/apache/xpath/NodeSet;->setShouldCacheNodes(Z)V
-Lorg/apache/xpath/NodeSetDTM;-><init>(Lorg/w3c/dom/NodeList;Lorg/apache/xpath/XPathContext;)V
-Lorg/apache/xpath/NodeSetDTM;-><init>(Lorg/w3c/dom/traversal/NodeIterator;Lorg/apache/xpath/XPathContext;)V
-Lorg/apache/xpath/NodeSetDTM;->addNode(I)V
-Lorg/apache/xpath/NodeSetDTM;->detach()V
-Lorg/apache/xpath/NodeSetDTM;->getLength()I
-Lorg/apache/xpath/NodeSetDTM;->item(I)I
-Lorg/apache/xpath/objects/XBoolean;-><init>(Z)V
-Lorg/apache/xpath/objects/XBoolean;->bool()Z
-Lorg/apache/xpath/objects/XBoolean;->str()Ljava/lang/String;
-Lorg/apache/xpath/objects/XBooleanStatic;-><init>(Z)V
-Lorg/apache/xpath/objects/XNodeSet;-><init>(ILorg/apache/xml/dtm/DTMManager;)V
-Lorg/apache/xpath/objects/XNodeSet;-><init>(Lorg/apache/xml/dtm/DTMIterator;)V
-Lorg/apache/xpath/objects/XNodeSet;-><init>(Lorg/apache/xml/dtm/DTMManager;)V
-Lorg/apache/xpath/objects/XNodeSet;->iterRaw()Lorg/apache/xml/dtm/DTMIterator;
-Lorg/apache/xpath/objects/XNodeSet;->mutableNodeset()Lorg/apache/xpath/NodeSetDTM;
-Lorg/apache/xpath/objects/XNodeSet;->nodelist()Lorg/w3c/dom/NodeList;
-Lorg/apache/xpath/objects/XNumber;-><init>(D)V
-Lorg/apache/xpath/objects/XNumber;->num()D
-Lorg/apache/xpath/objects/XNumber;->str()Ljava/lang/String;
-Lorg/apache/xpath/objects/XObject;->bool()Z
-Lorg/apache/xpath/objects/XObject;->create(Ljava/lang/Object;)Lorg/apache/xpath/objects/XObject;
-Lorg/apache/xpath/objects/XObject;->getType()I
-Lorg/apache/xpath/objects/XObject;->getTypeString()Ljava/lang/String;
-Lorg/apache/xpath/objects/XObject;->iter()Lorg/apache/xml/dtm/DTMIterator;
-Lorg/apache/xpath/objects/XObject;->nodelist()Lorg/w3c/dom/NodeList;
-Lorg/apache/xpath/objects/XObject;->nodeset()Lorg/w3c/dom/traversal/NodeIterator;
-Lorg/apache/xpath/objects/XObject;->num()D
-Lorg/apache/xpath/objects/XObject;->object()Ljava/lang/Object;
-Lorg/apache/xpath/objects/XObject;->str()Ljava/lang/String;
-Lorg/apache/xpath/objects/XObject;->xstr()Lorg/apache/xml/utils/XMLString;
-Lorg/apache/xpath/objects/XRTreeFrag;-><init>(ILorg/apache/xpath/XPathContext;)V
-Lorg/apache/xpath/objects/XRTreeFrag;->asNodeIterator()Lorg/apache/xml/dtm/DTMIterator;
-Lorg/apache/xpath/objects/XRTreeFrag;->convertToNodeset()Lorg/w3c/dom/NodeList;
-Lorg/apache/xpath/objects/XString;-><init>(Ljava/lang/String;)V
-Lorg/apache/xpath/objects/XString;->num()D
-Lorg/apache/xpath/patterns/NodeTest;->setWhatToShow(I)V
-Lorg/apache/xpath/res/XPATHErrorResources;-><init>()V
-Lorg/apache/xpath/res/XPATHMessages;->createXPATHMessage(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
-Lorg/apache/xpath/XPath;-><init>(Ljava/lang/String;Ljavax/xml/transform/SourceLocator;Lorg/apache/xml/utils/PrefixResolver;I)V
-Lorg/apache/xpath/XPath;-><init>(Ljava/lang/String;Ljavax/xml/transform/SourceLocator;Lorg/apache/xml/utils/PrefixResolver;ILjavax/xml/transform/ErrorListener;)V
-Lorg/apache/xpath/XPath;->execute(Lorg/apache/xpath/XPathContext;ILorg/apache/xml/utils/PrefixResolver;)Lorg/apache/xpath/objects/XObject;
-Lorg/apache/xpath/XPath;->execute(Lorg/apache/xpath/XPathContext;Lorg/w3c/dom/Node;Lorg/apache/xml/utils/PrefixResolver;)Lorg/apache/xpath/objects/XObject;
-Lorg/apache/xpath/XPath;->getPatternString()Ljava/lang/String;
-Lorg/apache/xpath/XPathAPI;->selectNodeList(Lorg/w3c/dom/Node;Ljava/lang/String;)Lorg/w3c/dom/NodeList;
-Lorg/apache/xpath/XPathAPI;->selectNodeList(Lorg/w3c/dom/Node;Ljava/lang/String;Lorg/w3c/dom/Node;)Lorg/w3c/dom/NodeList;
-Lorg/apache/xpath/XPathAPI;->selectSingleNode(Lorg/w3c/dom/Node;Ljava/lang/String;)Lorg/w3c/dom/Node;
-Lorg/apache/xpath/XPathAPI;->selectSingleNode(Lorg/w3c/dom/Node;Ljava/lang/String;Lorg/w3c/dom/Node;)Lorg/w3c/dom/Node;
-Lorg/apache/xpath/XPathContext$XPathExpressionContext;->getDTMManager()Lorg/apache/xml/dtm/DTMManager;
-Lorg/apache/xpath/XPathContext$XPathExpressionContext;->getXPathContext()Lorg/apache/xpath/XPathContext;
-Lorg/apache/xpath/XPathContext;-><init>()V
-Lorg/apache/xpath/XPathContext;-><init>(Ljava/lang/Object;)V
-Lorg/apache/xpath/XPathContext;->getAxesIteratorStackStacks()Ljava/util/Stack;
-Lorg/apache/xpath/XPathContext;->getContextNodeList()Lorg/apache/xml/dtm/DTMIterator;
-Lorg/apache/xpath/XPathContext;->getContextNodeListsStack()Ljava/util/Stack;
-Lorg/apache/xpath/XPathContext;->getCurrentExpressionNodeStack()Lorg/apache/xml/utils/IntStack;
-Lorg/apache/xpath/XPathContext;->getCurrentNode()I
-Lorg/apache/xpath/XPathContext;->getCurrentNodeStack()Lorg/apache/xml/utils/IntStack;
-Lorg/apache/xpath/XPathContext;->getDTM(I)Lorg/apache/xml/dtm/DTM;
-Lorg/apache/xpath/XPathContext;->getDTMHandleFromNode(Lorg/w3c/dom/Node;)I
-Lorg/apache/xpath/XPathContext;->getDTMManager()Lorg/apache/xml/dtm/DTMManager;
-Lorg/apache/xpath/XPathContext;->getExpressionContext()Lorg/apache/xalan/extensions/ExpressionContext;
-Lorg/apache/xpath/XPathContext;->getNamespaceContext()Lorg/apache/xml/utils/PrefixResolver;
-Lorg/apache/xpath/XPathContext;->getOwnerObject()Ljava/lang/Object;
-Lorg/apache/xpath/XPathContext;->getSAXLocator()Ljavax/xml/transform/SourceLocator;
-Lorg/apache/xpath/XPathContext;->getVarStack()Lorg/apache/xpath/VariableStack;
-Lorg/apache/xpath/XPathContext;->m_dtmManager:Lorg/apache/xml/dtm/DTMManager;
-Lorg/apache/xpath/XPathContext;->popContextNodeList()V
-Lorg/apache/xpath/XPathContext;->popCurrentNode()V
-Lorg/apache/xpath/XPathContext;->pushContextNodeList(Lorg/apache/xml/dtm/DTMIterator;)V
-Lorg/apache/xpath/XPathContext;->pushCurrentNode(I)V
-Lorg/apache/xpath/XPathContext;->reset()V
-Lorg/apache/xpath/XPathContext;->setAxesIteratorStackStacks(Ljava/util/Stack;)V
-Lorg/apache/xpath/XPathContext;->setContextNodeListsStack(Ljava/util/Stack;)V
-Lorg/apache/xpath/XPathContext;->setCurrentExpressionNodeStack(Lorg/apache/xml/utils/IntStack;)V
-Lorg/apache/xpath/XPathContext;->setCurrentNodeStack(Lorg/apache/xml/utils/IntStack;)V
-Lorg/apache/xpath/XPathContext;->setSecureProcessing(Z)V
-Lorg/apache/xpath/XPathContext;->setVarStack(Lorg/apache/xpath/VariableStack;)V
diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java
index d9987249..863fd36 100644
--- a/core/java/android/bluetooth/BluetoothProfileConnector.java
+++ b/core/java/android/bluetooth/BluetoothProfileConnector.java
@@ -32,12 +32,12 @@
* @hide
*/
public abstract class BluetoothProfileConnector<T> {
- private int mProfileId;
+ private final int mProfileId;
private BluetoothProfile.ServiceListener mServiceListener;
- private BluetoothProfile mProfileProxy;
+ private final BluetoothProfile mProfileProxy;
private Context mContext;
- private String mProfileName;
- private String mServiceName;
+ private final String mProfileName;
+ private final String mServiceName;
private volatile T mService;
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
@@ -65,7 +65,7 @@
logDebug("Proxy object disconnected");
doUnbind();
if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
+ mServiceListener.onServiceDisconnected(mProfileId);
}
}
};
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b09eada..cb939f0 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -807,7 +807,7 @@
*
* @hide
*/
- public static final int INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS = 0x00000200;
+ public static final int INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS = 0x00400000;
/** {@hide} */
public static final int INSTALL_FORCE_VOLUME_UUID = 0x00000200;
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 41be38a..58aacc2 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -153,14 +153,21 @@
public static final int CLONE_REMOVE_RES_NAMES = 1 << 3;
/** @hide */
+ public static final int CLONE_REMOVE_PERSON = 1 << 4;
+
+ /** @hide */
public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON | CLONE_REMOVE_RES_NAMES;
/** @hide */
public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT
- | CLONE_REMOVE_RES_NAMES;
+ | CLONE_REMOVE_RES_NAMES | CLONE_REMOVE_PERSON;
/** @hide */
public static final int CLONE_REMOVE_FOR_LAUNCHER_APPROVAL = CLONE_REMOVE_INTENT
+ | CLONE_REMOVE_RES_NAMES | CLONE_REMOVE_PERSON;
+
+ /** @hide */
+ public static final int CLONE_REMOVE_FOR_APP_PREDICTION = CLONE_REMOVE_ICON
| CLONE_REMOVE_RES_NAMES;
/** @hide */
@@ -169,8 +176,11 @@
CLONE_REMOVE_INTENT,
CLONE_REMOVE_NON_KEY_INFO,
CLONE_REMOVE_RES_NAMES,
+ CLONE_REMOVE_PERSON,
CLONE_REMOVE_FOR_CREATOR,
- CLONE_REMOVE_FOR_LAUNCHER
+ CLONE_REMOVE_FOR_LAUNCHER,
+ CLONE_REMOVE_FOR_LAUNCHER_APPROVAL,
+ CLONE_REMOVE_FOR_APP_PREDICTION
})
@Retention(RetentionPolicy.SOURCE)
public @interface CloneFlags {}
@@ -548,7 +558,9 @@
mDisabledMessage = source.mDisabledMessage;
mDisabledMessageResId = source.mDisabledMessageResId;
mCategories = cloneCategories(source.mCategories);
- mPersons = clonePersons(source.mPersons);
+ if ((cloneFlags & CLONE_REMOVE_PERSON) == 0) {
+ mPersons = clonePersons(source.mPersons);
+ }
if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
mIntents = cloneIntents(source.mIntents);
mIntentPersistableExtrases =
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index e892b65..bb344e2 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -18,6 +18,7 @@
import static android.os.Process.CLAT_UID;
+import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,6 +34,7 @@
import java.io.CharArrayWriter;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.function.Predicate;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
@@ -993,23 +995,33 @@
if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) {
return;
}
+ filter(e -> (limitUid == UID_ALL || limitUid == e.uid)
+ && (limitTag == TAG_ALL || limitTag == e.tag)
+ && (limitIfaces == INTERFACES_ALL
+ || ArrayUtils.contains(limitIfaces, e.iface)));
+ }
+ /**
+ * Only keep entries with {@link #set} value less than {@link #SET_DEBUG_START}.
+ *
+ * <p>This mutates the original structure in place.
+ */
+ public void filterDebugEntries() {
+ filter(e -> e.set < SET_DEBUG_START);
+ }
+
+ private void filter(Predicate<Entry> predicate) {
Entry entry = new Entry();
int nextOutputEntry = 0;
for (int i = 0; i < size; i++) {
entry = getValues(i, entry);
- final boolean matches =
- (limitUid == UID_ALL || limitUid == entry.uid)
- && (limitTag == TAG_ALL || limitTag == entry.tag)
- && (limitIfaces == INTERFACES_ALL
- || ArrayUtils.contains(limitIfaces, entry.iface));
-
- if (matches) {
- setValues(nextOutputEntry, entry);
+ if (predicate.test(entry)) {
+ if (nextOutputEntry != i) {
+ setValues(nextOutputEntry, entry);
+ }
nextOutputEntry++;
}
}
-
size = nextOutputEntry;
}
@@ -1175,133 +1187,221 @@
/**
* VPN accounting. Move some VPN's underlying traffic to other UIDs that use tun0 iface.
*
- * This method should only be called on delta NetworkStats. Do not call this method on a
- * snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may
- * change over time.
+ * <p>This method should only be called on delta NetworkStats. Do not call this method on a
+ * snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may change
+ * over time.
*
- * This method performs adjustments for one active VPN package and one VPN iface at a time.
- *
- * It is possible for the VPN software to use multiple underlying networks. This method
- * only migrates traffic for the primary underlying network.
+ * <p>This method performs adjustments for one active VPN package and one VPN iface at a time.
*
* @param tunUid uid of the VPN application
* @param tunIface iface of the vpn tunnel
- * @param underlyingIface the primary underlying network iface used by the VPN application
- * @return true if it successfully adjusts the accounting for VPN, false otherwise
+ * @param underlyingIfaces underlying network ifaces used by the VPN application
*/
- public boolean migrateTun(int tunUid, String tunIface, String underlyingIface) {
- Entry tunIfaceTotal = new Entry();
- Entry underlyingIfaceTotal = new Entry();
+ public void migrateTun(int tunUid, @NonNull String tunIface,
+ @NonNull String[] underlyingIfaces) {
+ // Combined usage by all apps using VPN.
+ final Entry tunIfaceTotal = new Entry();
+ // Usage by VPN, grouped by its {@code underlyingIfaces}.
+ final Entry[] perInterfaceTotal = new Entry[underlyingIfaces.length];
+ // Usage by VPN, summed across all its {@code underlyingIfaces}.
+ final Entry underlyingIfacesTotal = new Entry();
- tunAdjustmentInit(tunUid, tunIface, underlyingIface, tunIfaceTotal, underlyingIfaceTotal);
+ for (int i = 0; i < perInterfaceTotal.length; i++) {
+ perInterfaceTotal[i] = new Entry();
+ }
- // If tunIface < underlyingIface, it leaves the overhead traffic in the VPN app.
- // If tunIface > underlyingIface, the VPN app doesn't get credit for data compression.
+ tunAdjustmentInit(tunUid, tunIface, underlyingIfaces, tunIfaceTotal, perInterfaceTotal,
+ underlyingIfacesTotal);
+
+ // If tunIface < underlyingIfacesTotal, it leaves the overhead traffic in the VPN app.
+ // If tunIface > underlyingIfacesTotal, the VPN app doesn't get credit for data compression.
// Negative stats should be avoided.
- Entry pool = tunGetPool(tunIfaceTotal, underlyingIfaceTotal);
- if (pool.isEmpty()) {
- return true;
- }
- Entry moved =
- addTrafficToApplications(tunUid, tunIface, underlyingIface, tunIfaceTotal, pool);
- deductTrafficFromVpnApp(tunUid, underlyingIface, moved);
-
- if (!moved.isEmpty()) {
- Slog.wtf(TAG, "Failed to deduct underlying network traffic from VPN package. Moved="
- + moved);
- return false;
- }
- return true;
+ final Entry[] moved =
+ addTrafficToApplications(tunUid, tunIface, underlyingIfaces, tunIfaceTotal,
+ perInterfaceTotal, underlyingIfacesTotal);
+ deductTrafficFromVpnApp(tunUid, underlyingIfaces, moved);
}
/**
* Initializes the data used by the migrateTun() method.
*
- * This is the first pass iteration which does the following work:
- * (1) Adds up all the traffic through the tunUid's underlyingIface
- * (both foreground and background).
- * (2) Adds up all the traffic through tun0 excluding traffic from the vpn app itself.
+ * <p>This is the first pass iteration which does the following work:
+ *
+ * <ul>
+ * <li>Adds up all the traffic through the tunUid's underlyingIfaces (both foreground and
+ * background).
+ * <li>Adds up all the traffic through tun0 excluding traffic from the vpn app itself.
+ * </ul>
+ *
+ * @param tunUid uid of the VPN application
+ * @param tunIface iface of the vpn tunnel
+ * @param underlyingIfaces underlying network ifaces used by the VPN application
+ * @param tunIfaceTotal output parameter; combined data usage by all apps using VPN
+ * @param perInterfaceTotal output parameter; data usage by VPN app, grouped by its {@code
+ * underlyingIfaces}
+ * @param underlyingIfacesTotal output parameter; data usage by VPN, summed across all of its
+ * {@code underlyingIfaces}
*/
- private void tunAdjustmentInit(int tunUid, String tunIface, String underlyingIface,
- Entry tunIfaceTotal, Entry underlyingIfaceTotal) {
- Entry recycle = new Entry();
+ private void tunAdjustmentInit(int tunUid, @NonNull String tunIface,
+ @NonNull String[] underlyingIfaces, @NonNull Entry tunIfaceTotal,
+ @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal) {
+ final Entry recycle = new Entry();
for (int i = 0; i < size; i++) {
getValues(i, recycle);
if (recycle.uid == UID_ALL) {
throw new IllegalStateException(
"Cannot adjust VPN accounting on an iface aggregated NetworkStats.");
- } if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) {
+ }
+ if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) {
throw new IllegalStateException(
"Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*");
}
-
- if (recycle.uid == tunUid && recycle.tag == TAG_NONE
- && Objects.equals(underlyingIface, recycle.iface)) {
- underlyingIfaceTotal.add(recycle);
+ if (recycle.tag != TAG_NONE) {
+ // TODO(b/123666283): Take all tags for tunUid into account.
+ continue;
}
- if (recycle.uid != tunUid && recycle.tag == TAG_NONE
- && Objects.equals(tunIface, recycle.iface)) {
+ if (recycle.uid == tunUid) {
+ // Add up traffic through tunUid's underlying interfaces.
+ for (int j = 0; j < underlyingIfaces.length; j++) {
+ if (Objects.equals(underlyingIfaces[j], recycle.iface)) {
+ perInterfaceTotal[j].add(recycle);
+ underlyingIfacesTotal.add(recycle);
+ break;
+ }
+ }
+ } else if (tunIface.equals(recycle.iface)) {
// Add up all tunIface traffic excluding traffic from the vpn app itself.
tunIfaceTotal.add(recycle);
}
}
}
- private static Entry tunGetPool(Entry tunIfaceTotal, Entry underlyingIfaceTotal) {
- Entry pool = new Entry();
- pool.rxBytes = Math.min(tunIfaceTotal.rxBytes, underlyingIfaceTotal.rxBytes);
- pool.rxPackets = Math.min(tunIfaceTotal.rxPackets, underlyingIfaceTotal.rxPackets);
- pool.txBytes = Math.min(tunIfaceTotal.txBytes, underlyingIfaceTotal.txBytes);
- pool.txPackets = Math.min(tunIfaceTotal.txPackets, underlyingIfaceTotal.txPackets);
- pool.operations = Math.min(tunIfaceTotal.operations, underlyingIfaceTotal.operations);
- return pool;
- }
+ /**
+ * Distributes traffic across apps that are using given {@code tunIface}, and returns the total
+ * traffic that should be moved off of {@code tunUid} grouped by {@code underlyingIfaces}.
+ *
+ * @param tunUid uid of the VPN application
+ * @param tunIface iface of the vpn tunnel
+ * @param underlyingIfaces underlying network ifaces used by the VPN application
+ * @param tunIfaceTotal combined data usage across all apps using {@code tunIface}
+ * @param perInterfaceTotal data usage by VPN app, grouped by its {@code underlyingIfaces}
+ * @param underlyingIfacesTotal data usage by VPN, summed across all of its {@code
+ * underlyingIfaces}
+ */
+ private Entry[] addTrafficToApplications(int tunUid, @NonNull String tunIface,
+ @NonNull String[] underlyingIfaces, @NonNull Entry tunIfaceTotal,
+ @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal) {
+ // Traffic that should be moved off of each underlying interface for tunUid (see
+ // deductTrafficFromVpnApp below).
+ final Entry[] moved = new Entry[underlyingIfaces.length];
+ for (int i = 0; i < underlyingIfaces.length; i++) {
+ moved[i] = new Entry();
+ }
- private Entry addTrafficToApplications(int tunUid, String tunIface, String underlyingIface,
- Entry tunIfaceTotal, Entry pool) {
- Entry moved = new Entry();
- Entry tmpEntry = new Entry();
- tmpEntry.iface = underlyingIface;
- for (int i = 0; i < size; i++) {
- // the vpn app is excluded from the redistribution but all moved traffic will be
- // deducted from the vpn app (see deductTrafficFromVpnApp below).
- if (Objects.equals(iface[i], tunIface) && uid[i] != tunUid) {
- if (tunIfaceTotal.rxBytes > 0) {
- tmpEntry.rxBytes = pool.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes;
- } else {
- tmpEntry.rxBytes = 0;
- }
- if (tunIfaceTotal.rxPackets > 0) {
- tmpEntry.rxPackets = pool.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets;
- } else {
- tmpEntry.rxPackets = 0;
- }
- if (tunIfaceTotal.txBytes > 0) {
- tmpEntry.txBytes = pool.txBytes * txBytes[i] / tunIfaceTotal.txBytes;
- } else {
- tmpEntry.txBytes = 0;
- }
- if (tunIfaceTotal.txPackets > 0) {
- tmpEntry.txPackets = pool.txPackets * txPackets[i] / tunIfaceTotal.txPackets;
- } else {
- tmpEntry.txPackets = 0;
- }
- if (tunIfaceTotal.operations > 0) {
- tmpEntry.operations =
- pool.operations * operations[i] / tunIfaceTotal.operations;
- } else {
- tmpEntry.operations = 0;
- }
- tmpEntry.uid = uid[i];
- tmpEntry.tag = tag[i];
+ final Entry tmpEntry = new Entry();
+ final int origSize = size;
+ for (int i = 0; i < origSize; i++) {
+ if (!Objects.equals(iface[i], tunIface)) {
+ // Consider only entries that go onto the VPN interface.
+ continue;
+ }
+ if (uid[i] == tunUid) {
+ // Exclude VPN app from the redistribution, as it can choose to create packet
+ // streams by writing to itself.
+ continue;
+ }
+ tmpEntry.uid = uid[i];
+ tmpEntry.tag = tag[i];
+ tmpEntry.metered = metered[i];
+ tmpEntry.roaming = roaming[i];
+ tmpEntry.defaultNetwork = defaultNetwork[i];
+
+ // In a first pass, compute this entry's total share of data across all
+ // underlyingIfaces. This is computed on the basis of the share of this entry's usage
+ // over tunIface.
+ // TODO: Consider refactoring first pass into a separate helper method.
+ long totalRxBytes = 0;
+ if (tunIfaceTotal.rxBytes > 0) {
+ // Note - The multiplication below should not overflow since NetworkStatsService
+ // processes this every time device has transmitted/received amount equivalent to
+ // global threshold alert (~ 2MB) across all interfaces.
+ final long rxBytesAcrossUnderlyingIfaces =
+ underlyingIfacesTotal.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes;
+ // app must not be blamed for more than it consumed on tunIface
+ totalRxBytes = Math.min(rxBytes[i], rxBytesAcrossUnderlyingIfaces);
+ }
+ long totalRxPackets = 0;
+ if (tunIfaceTotal.rxPackets > 0) {
+ final long rxPacketsAcrossUnderlyingIfaces =
+ underlyingIfacesTotal.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets;
+ totalRxPackets = Math.min(rxPackets[i], rxPacketsAcrossUnderlyingIfaces);
+ }
+ long totalTxBytes = 0;
+ if (tunIfaceTotal.txBytes > 0) {
+ final long txBytesAcrossUnderlyingIfaces =
+ underlyingIfacesTotal.txBytes * txBytes[i] / tunIfaceTotal.txBytes;
+ totalTxBytes = Math.min(txBytes[i], txBytesAcrossUnderlyingIfaces);
+ }
+ long totalTxPackets = 0;
+ if (tunIfaceTotal.txPackets > 0) {
+ final long txPacketsAcrossUnderlyingIfaces =
+ underlyingIfacesTotal.txPackets * txPackets[i] / tunIfaceTotal.txPackets;
+ totalTxPackets = Math.min(txPackets[i], txPacketsAcrossUnderlyingIfaces);
+ }
+ long totalOperations = 0;
+ if (tunIfaceTotal.operations > 0) {
+ final long operationsAcrossUnderlyingIfaces =
+ underlyingIfacesTotal.operations * operations[i] / tunIfaceTotal.operations;
+ totalOperations = Math.min(operations[i], operationsAcrossUnderlyingIfaces);
+ }
+ // In a second pass, distribute these values across interfaces in the proportion that
+ // each interface represents of the total traffic of the underlying interfaces.
+ for (int j = 0; j < underlyingIfaces.length; j++) {
+ tmpEntry.iface = underlyingIfaces[j];
+ tmpEntry.rxBytes = 0;
+ // Reset 'set' to correct value since it gets updated when adding debug info below.
tmpEntry.set = set[i];
- tmpEntry.metered = metered[i];
- tmpEntry.roaming = roaming[i];
- tmpEntry.defaultNetwork = defaultNetwork[i];
+ if (underlyingIfacesTotal.rxBytes > 0) {
+ tmpEntry.rxBytes =
+ totalRxBytes
+ * perInterfaceTotal[j].rxBytes
+ / underlyingIfacesTotal.rxBytes;
+ }
+ tmpEntry.rxPackets = 0;
+ if (underlyingIfacesTotal.rxPackets > 0) {
+ tmpEntry.rxPackets =
+ totalRxPackets
+ * perInterfaceTotal[j].rxPackets
+ / underlyingIfacesTotal.rxPackets;
+ }
+ tmpEntry.txBytes = 0;
+ if (underlyingIfacesTotal.txBytes > 0) {
+ tmpEntry.txBytes =
+ totalTxBytes
+ * perInterfaceTotal[j].txBytes
+ / underlyingIfacesTotal.txBytes;
+ }
+ tmpEntry.txPackets = 0;
+ if (underlyingIfacesTotal.txPackets > 0) {
+ tmpEntry.txPackets =
+ totalTxPackets
+ * perInterfaceTotal[j].txPackets
+ / underlyingIfacesTotal.txPackets;
+ }
+ tmpEntry.operations = 0;
+ if (underlyingIfacesTotal.operations > 0) {
+ tmpEntry.operations =
+ totalOperations
+ * perInterfaceTotal[j].operations
+ / underlyingIfacesTotal.operations;
+ }
+ // tmpEntry now contains the migrated data of the i-th entry for the j-th underlying
+ // interface. Add that data usage to this object.
combineValues(tmpEntry);
if (tag[i] == TAG_NONE) {
- moved.add(tmpEntry);
+ // Add the migrated data to moved so it is deducted from the VPN app later.
+ moved[j].add(tmpEntry);
// Add debug info
tmpEntry.set = SET_DBG_VPN_IN;
combineValues(tmpEntry);
@@ -1311,38 +1411,45 @@
return moved;
}
- private void deductTrafficFromVpnApp(int tunUid, String underlyingIface, Entry moved) {
- // Add debug info
- moved.uid = tunUid;
- moved.set = SET_DBG_VPN_OUT;
- moved.tag = TAG_NONE;
- moved.iface = underlyingIface;
- moved.metered = METERED_ALL;
- moved.roaming = ROAMING_ALL;
- moved.defaultNetwork = DEFAULT_NETWORK_ALL;
- combineValues(moved);
+ private void deductTrafficFromVpnApp(
+ int tunUid,
+ @NonNull String[] underlyingIfaces,
+ @NonNull Entry[] moved) {
+ for (int i = 0; i < underlyingIfaces.length; i++) {
+ moved[i].uid = tunUid;
+ // Add debug info
+ moved[i].set = SET_DBG_VPN_OUT;
+ moved[i].tag = TAG_NONE;
+ moved[i].iface = underlyingIfaces[i];
+ moved[i].metered = METERED_ALL;
+ moved[i].roaming = ROAMING_ALL;
+ moved[i].defaultNetwork = DEFAULT_NETWORK_ALL;
+ combineValues(moved[i]);
- // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
- // the TAG_NONE traffic.
- //
- // Relies on the fact that the underlying traffic only has state ROAMING_NO and METERED_NO,
- // which should be the case as it comes directly from the /proc file. We only blend in the
- // roaming data after applying these adjustments, by checking the NetworkIdentity of the
- // underlying iface.
- int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
- if (idxVpnBackground != -1) {
- tunSubtract(idxVpnBackground, this, moved);
- }
+ // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
+ // the TAG_NONE traffic.
+ //
+ // Relies on the fact that the underlying traffic only has state ROAMING_NO and
+ // METERED_NO, which should be the case as it comes directly from the /proc file.
+ // We only blend in the roaming data after applying these adjustments, by checking the
+ // NetworkIdentity of the underlying iface.
+ final int idxVpnBackground = findIndex(underlyingIfaces[i], tunUid, SET_DEFAULT,
+ TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
+ if (idxVpnBackground != -1) {
+ // Note - tunSubtract also updates moved[i]; whatever traffic that's left is removed
+ // from foreground usage.
+ tunSubtract(idxVpnBackground, this, moved[i]);
+ }
- int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
- if (idxVpnForeground != -1) {
- tunSubtract(idxVpnForeground, this, moved);
+ final int idxVpnForeground = findIndex(underlyingIfaces[i], tunUid, SET_FOREGROUND,
+ TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
+ if (idxVpnForeground != -1) {
+ tunSubtract(idxVpnForeground, this, moved[i]);
+ }
}
}
- private static void tunSubtract(int i, NetworkStats left, Entry right) {
+ private static void tunSubtract(int i, @NonNull NetworkStats left, @NonNull Entry right) {
long rxBytes = Math.min(left.rxBytes[i], right.rxBytes);
left.rxBytes[i] -= rxBytes;
right.rxBytes -= rxBytes;
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 5039b31..24a1477 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -231,13 +231,6 @@
}
/**
- * Check whether application is profileable
- */
- private static boolean isProfileable(Context context) {
- return context.getApplicationInfo().isProfileableByShell();
- }
-
- /**
* Store the layer paths available to the loader.
*/
public void setLayerPaths(ClassLoader classLoader,
@@ -287,11 +280,11 @@
String layerPaths = "";
// Only enable additional debug functionality if the following conditions are met:
- // 1. App is debuggable, profileable, or device is rooted
+ // 1. App is debuggable or device is rooted
// 2. ENABLE_GPU_DEBUG_LAYERS is true
// 3. Package name is equal to GPU_DEBUG_APP
- if (isDebuggable(context) || isProfileable(context) || (getCanLoadSystemLibraries() == 1)) {
+ if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) {
final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
@@ -473,9 +466,8 @@
*/
private String getAngleDebugPackage(Context context, Bundle coreSettings) {
final boolean appIsDebuggable = isDebuggable(context);
- final boolean appIsProfileable = isProfileable(context);
final boolean deviceIsDebuggable = getCanLoadSystemLibraries() == 1;
- if (appIsDebuggable || appIsProfileable || deviceIsDebuggable) {
+ if (appIsDebuggable || deviceIsDebuggable) {
String debugPackage;
if (coreSettings != null) {
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index de963c9..fe2e948 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -2001,6 +2002,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
public final int readExceptionCode() {
int code = readInt();
if (code == EX_HAS_REPLY_HEADER) {
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index edfdda8..4538410 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -174,6 +174,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static boolean getBoolean(@NonNull String key, boolean def) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_boolean(key, def);
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index f1f24fb..921f0f2 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -67,6 +68,7 @@
* @hide
*/
@SystemApi
+@TestApi
public class DynamicSystemClient {
/** @hide */
@IntDef(prefix = { "STATUS_" }, value = {
@@ -283,6 +285,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public DynamicSystemClient(@NonNull Context context) {
mContext = context;
mConnection = new DynSystemServiceConnection();
@@ -314,8 +317,11 @@
* Bind to {@code DynamicSystem} installation service. Binding to the installation service
* allows it to send status updates to {@link #OnStatusChangedListener}. It is recommanded
* to bind before calling {@link #start} and get status updates.
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
+ @SystemApi
+ @TestApi
public void bind() {
if (!featureFlagEnabled()) {
Slog.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; bind() aborted.");
@@ -334,8 +340,11 @@
/**
* Unbind from {@code DynamicSystem} installation service. Unbinding from the installation
* service stops it from sending following status updates.
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
+ @SystemApi
+ @TestApi
public void unbind() {
if (!mBound) {
return;
@@ -367,8 +376,11 @@
*
* @param systemUrl a network Uri, a file Uri or a content Uri pointing to a system image file.
* @param systemSize size of system image.
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
+ @SystemApi
+ @TestApi
public void start(@NonNull Uri systemUrl, @BytesLong long systemSize) {
start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 66b9d168..0db5c36 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8081,6 +8081,15 @@
"lock_screen_show_silent_notifications";
/**
+ * Indicates whether snooze options should be shown on notifications
+ * <p>
+ * Type: int (0 for false, 1 for true)
+ *
+ * @hide
+ */
+ public static final String SHOW_NOTIFICATION_SNOOZE = "show_notification_snooze";
+
+ /**
* List of TV inputs that are currently hidden. This is a string
* containing the IDs of all hidden TV inputs. Each ID is encoded by
* {@link android.net.Uri#encode(String)} and separated by ':'.
@@ -8968,6 +8977,7 @@
LOCK_SCREEN_CUSTOM_CLOCK_FACE,
LOCK_SCREEN_SHOW_NOTIFICATIONS,
LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
+ SHOW_NOTIFICATION_SNOOZE,
ZEN_DURATION,
SHOW_ZEN_UPGRADE_NOTIFICATION,
SHOW_ZEN_SETTINGS_SUGGESTION,
@@ -9150,6 +9160,7 @@
VALIDATORS.put(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, BOOLEAN_VALIDATOR);
VALIDATORS.put(LOCK_SCREEN_SHOW_NOTIFICATIONS, BOOLEAN_VALIDATOR);
VALIDATORS.put(LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(SHOW_NOTIFICATION_SNOOZE, BOOLEAN_VALIDATOR);
VALIDATORS.put(ZEN_DURATION, ZEN_DURATION_VALIDATOR);
VALIDATORS.put(SHOW_ZEN_UPGRADE_NOTIFICATION, BOOLEAN_VALIDATOR);
VALIDATORS.put(SHOW_ZEN_SETTINGS_SUGGESTION, BOOLEAN_VALIDATOR);
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index c42dc81..324e02c 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -16,6 +16,7 @@
package android.util;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.SystemProperties;
import android.provider.Settings;
@@ -29,6 +30,7 @@
*
* @hide
*/
+@TestApi
public class FeatureFlagUtils {
public static final String FFLAG_PREFIX = "sys.fflag.";
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 9436633..73e0e4b 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1301,7 +1301,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
final boolean isOutside =
- event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event)
+ event.getAction() == MotionEvent.ACTION_UP && isOutOfBounds(context, event)
|| event.getAction() == MotionEvent.ACTION_OUTSIDE;
if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) {
return true;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index fca97fe..ee99837 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1434,6 +1434,22 @@
List<ShortcutManager.ShareShortcutInfo> resultList,
List<DisplayResolveInfo> driList,
@Nullable List<AppTarget> appTargets) {
+ if (appTargets != null && appTargets.size() != resultList.size()) {
+ throw new RuntimeException("resultList and appTargets must have the same size."
+ + " resultList.size()=" + resultList.size()
+ + " appTargets.size()=" + appTargets.size());
+ }
+
+ for (int i = resultList.size() - 1; i >= 0; i--) {
+ final String packageName = resultList.get(i).getTargetComponent().getPackageName();
+ if (!isPackageEnabled(packageName)) {
+ resultList.remove(i);
+ if (appTargets != null) {
+ appTargets.remove(i);
+ }
+ }
+ }
+
// Match ShareShortcutInfos with DisplayResolveInfos to be able to use the old code path
// for direct share targets. After ShareSheet is refactored we should use the
// ShareShortcutInfos directly.
@@ -1447,7 +1463,6 @@
ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo);
chooserTargets.add(chooserTarget);
if (mDirectShareAppTargetCache != null && appTargets != null) {
- // Note that appTargets.size() == resultList.size() is always true.
mDirectShareAppTargetCache.put(chooserTarget, appTargets.get(j));
}
}
@@ -1473,6 +1488,24 @@
mChooserHandler.sendMessage(msg);
}
+ private boolean isPackageEnabled(String packageName) {
+ if (TextUtils.isEmpty(packageName)) {
+ return false;
+ }
+ ApplicationInfo appInfo;
+ try {
+ appInfo = getPackageManager().getApplicationInfo(packageName, 0);
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+
+ if (appInfo != null && appInfo.enabled
+ && (appInfo.flags & ApplicationInfo.FLAG_SUSPENDED) == 0) {
+ return true;
+ }
+ return false;
+ }
+
private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut) {
ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
Bundle extras = new Bundle();
@@ -1603,7 +1636,8 @@
*/
@Nullable
private AppPredictor getAppPredictorForDirectShareIfEnabled() {
- return USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS ? getAppPredictor() : null;
+ return USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS && !ActivityManager.isLowRamDeviceStatic()
+ ? getAppPredictor() : null;
}
/**
@@ -2349,6 +2383,8 @@
@Override
public void onListRebuilt() {
+ updateAlphabeticalList();
+
// don't support direct share on low ram devices
if (ActivityManager.isLowRamDeviceStatic()) {
return;
@@ -2379,7 +2415,6 @@
queryTargetServices(this);
}
- updateAlphabeticalList();
}
@Override
@@ -2830,7 +2865,7 @@
// There can be at most one row in the listview, that is internally
// a ViewGroup with 2 rows
public int getServiceTargetRowCount() {
- if (isSendAction(getTargetIntent())) {
+ if (isSendAction(getTargetIntent()) && !ActivityManager.isLowRamDeviceStatic()) {
return 1;
}
return 0;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 538c81d..0a01beb 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -383,9 +383,11 @@
mSystemWindowInsets.right, 0);
View emptyView = findViewById(R.id.empty);
- emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
- + getResources().getDimensionPixelSize(
- R.dimen.chooser_edge_margin_normal) * 2);
+ if (emptyView != null) {
+ emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
+ + getResources().getDimensionPixelSize(
+ R.dimen.chooser_edge_margin_normal) * 2);
+ }
if (mFooterSpacer == null) {
mFooterSpacer = new Space(getApplicationContext());
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 14fe6ab..28ac8cc 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -113,5 +113,25 @@
*/
public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
+ // Flags related to Assistant Handles
+
+ /**
+ * (String) Which behavior mode for the Assistant Handles to use.
+ */
+ public static final String ASSIST_HANDLES_BEHAVIOR_MODE = "assist_handles_behavior_mode";
+
+ /**
+ * (long) How long, in milliseconds, to display Assist Handles when showing them temporarily.
+ */
+ public static final String ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS =
+ "assist_handles_show_and_go_duration_ms";
+
+ /**
+ * (long) How long, in milliseconds, to wait before displaying Assist Handles temporarily after
+ * hiding them.
+ */
+ public static final String ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS =
+ "assist_handles_shown_frequency_threshold_ms";
+
private SystemUiDeviceConfigFlags() { }
}
diff --git a/core/java/com/android/internal/net/VpnInfo.java b/core/java/com/android/internal/net/VpnInfo.java
index b1a41287..e74af5e 100644
--- a/core/java/com/android/internal/net/VpnInfo.java
+++ b/core/java/com/android/internal/net/VpnInfo.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Arrays;
+
/**
* A lightweight container used to carry information of the ongoing VPN.
* Internal use only..
@@ -28,14 +30,14 @@
public class VpnInfo implements Parcelable {
public int ownerUid;
public String vpnIface;
- public String primaryUnderlyingIface;
+ public String[] underlyingIfaces;
@Override
public String toString() {
return "VpnInfo{"
+ "ownerUid=" + ownerUid
+ ", vpnIface='" + vpnIface + '\''
- + ", primaryUnderlyingIface='" + primaryUnderlyingIface + '\''
+ + ", underlyingIfaces='" + Arrays.toString(underlyingIfaces) + '\''
+ '}';
}
@@ -48,7 +50,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(ownerUid);
dest.writeString(vpnIface);
- dest.writeString(primaryUnderlyingIface);
+ dest.writeStringArray(underlyingIfaces);
}
public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() {
@@ -57,7 +59,7 @@
VpnInfo info = new VpnInfo();
info.ownerUid = source.readInt();
info.vpnIface = source.readString();
- info.primaryUnderlyingIface = source.readString();
+ info.underlyingIfaces = source.readStringArray();
return info;
}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 3f8ddff..2873379 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -273,9 +273,11 @@
// The maximum number of jobs an app can run within this particular standby bucket's
// window size.
optional int32 max_job_count_rare = 11;
+ // The period of time used to rate limit recently run jobs.
+ optional int32 rate_limiting_window_ms = 19;
// The maximum number of jobs that should be allowed to run in the past
- // {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
- optional int32 max_job_count_per_allowed_time = 12;
+ // rate_limiting_window_ms.
+ optional int32 max_job_count_per_rate_limiting_window = 12;
// The maximum number of timing sessions an app can run within this particular standby
// bucket's window size.
optional int32 max_session_count_active = 13;
@@ -289,8 +291,8 @@
// bucket's window size.
optional int32 max_session_count_rare = 16;
// The maximum number of timing sessions that should be allowed to run in the past
- // {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
- optional int32 max_session_count_per_allowed_time = 17;
+ // rate_limiting_window_ms.
+ optional int32 max_session_count_per_rate_limiting_window = 17;
// Treat two distinct {@link TimingSession}s as the same if they start and end within this
// amount of time of each other.
optional int64 timing_session_coalescing_duration_ms = 18;
@@ -517,63 +519,56 @@
optional int64 expiration_time_elapsed = 2;
optional int64 window_size_ms = 3;
- /** The total amount of time the app ran in its respective bucket window size. */
+ optional int32 job_count_limit = 14;
+ optional int32 session_count_limit = 15;
+
+ // The total amount of time the app ran in its respective bucket window size.
optional int64 execution_time_in_window_ms = 4;
optional int32 bg_job_count_in_window = 5;
- /**
- * The total amount of time the app ran in the last
- * {@link QuotaController#MAX_PERIOD_MS}.
- */
+ // The total amount of time the app ran in the last
+ // {@link QuotaController#MAX_PERIOD_MS}.
optional int64 execution_time_in_max_period_ms = 6;
optional int32 bg_job_count_in_max_period = 7;
- /**
- * The number of {@link TimingSession}s within the bucket window size. This will include
- * sessions that started before the window as long as they end within the window.
- */
+ // The number of {@link TimingSession}s within the bucket window size. This will include
+ // sessions that started before the window as long as they end within the window.
optional int32 session_count_in_window = 11;
- /**
- * The time after which the sum of all the app's sessions plus
- * ConstantsProto.QuotaController.in_quota_buffer_ms equals the quota. This is only
- * valid if
- * execution_time_in_window_ms >=
- * ConstantsProto.QuotaController.allowed_time_per_period_ms
- * or
- * execution_time_in_max_period_ms >=
- * ConstantsProto.QuotaController.max_execution_time_ms.
- */
- optional int64 quota_cutoff_time_elapsed = 8;
+ // The time after which the app will be under the bucket quota. This is only valid if
+ // execution_time_in_window_ms >=
+ // ConstantsProto.QuotaController.allowed_time_per_period_ms
+ // or
+ // execution_time_in_max_period_ms >=
+ // ConstantsProto.QuotaController.max_execution_time_ms
+ // or
+ // bg_job_count_in_window >= job_count_limit
+ // or
+ // session_count_in_window >= session_count_limit.
+ optional int64 in_quota_time_elapsed = 8;
- /**
- * The time after which job_count_in_allowed_time should be considered invalid, in the
- * elapsed realtime timebase.
- */
+ // The time after which job_count_in_rate_limiting_window should be considered invalid,
+ // in the elapsed realtime timebase.
optional int64 job_count_expiration_time_elapsed = 9;
- /**
- * The number of jobs that ran in at least the last
- * ConstantsProto.QuotaController.allowed_time_per_period_ms.
- * It may contain a few stale entries since cleanup won't happen exactly every
- * ConstantsProto.QuotaController.allowed_time_per_period_ms.
- */
- optional int32 job_count_in_allowed_time = 10;
+ // The number of jobs that ran in at least the last
+ // ConstantsProto.QuotaController.rate_limiting_window_ms.
+ // It may contain a few stale entries since cleanup won't happen exactly every
+ // ConstantsProto.QuotaController.rate_limiting_window_ms. This should only be
+ // considered valid before elapsed realtime has reached
+ // job_count_expiration_time_elapsed.
+ optional int32 job_count_in_rate_limiting_window = 10;
- /**
- * The time after which {@link #timingSessionCountInAllowedTime} should be considered
- * invalid, in the elapsed realtime timebase.
- */
+ // The time after which {@link #timingSessionCountInAllowedTime} should be considered
+ // invalid, in the elapsed realtime timebase.
optional int64 session_count_expiration_time_elapsed = 12;
- /**
- * The number of {@link TimingSession}s that ran in at least the last
- * {@link #mAllowedTimePerPeriodMs}. It may contain a few stale entries since cleanup won't
- * happen exactly every {@link #mAllowedTimePerPeriodMs}. This should only be considered
- * valid before elapsed realtime has reached
- * {@link #timingSessionCountExpirationTimeElapsed}.
- */
- optional int32 session_count_in_allowed_time = 13;
+ // The number of {@link TimingSession}s that ran in at least the last
+ // ConstantsProto.QuotaController.rate_limiting_window_ms. It may contain a few stale
+ // entries since cleanup won't happen exactly every
+ // ConstantsProto.QuotaController.rate_limiting_window_ms. This should only be considered
+ // valid before elapsed realtime has reached session_count_expiration_time_elapsed.
+ optional int32 session_count_in_rate_limiting_window = 13;
}
message Package {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ab7aed3..890ad5e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2337,7 +2337,7 @@
The app can check whether it has this authorization by calling
{@link android.provider.Settings#canDrawOverlays
Settings.canDrawOverlays()}.
- <p>Protection level: signature -->
+ <p>Protection level: signature|preinstalled|appop|pre23|development -->
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
android:label="@string/permlab_systemAlertWindow"
android:description="@string/permdesc_systemAlertWindow"
@@ -2522,7 +2522,7 @@
can check whether it has this authorization by calling {@link
android.provider.Settings.System#canWrite Settings.System.canWrite()}.
- <p>Protection level: signature
+ <p>Protection level: signature|preinstalled|appop|pre23
-->
<permission android:name="android.permission.WRITE_SETTINGS"
android:label="@string/permlab_writeSettings"
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 6289262..b901048 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1473,10 +1473,14 @@
<item name="colorError">@color/error_color_device_default_light</item>
<item name="colorEdgeEffect">@color/edge_effect_device_default_light</item>
- <!-- Add white nav bar with divider that matches material -->
+ <!-- Add divider that matches material -->
<item name="navigationBarDividerColor">@color/navigation_bar_divider_device_default_settings</item>
- <item name="navigationBarColor">@android:color/white</item>
+
+ <!-- Add transparent nav and status bars with light icons to support drawing edge-to-edge
+ for Q gestural navigation-->
+ <item name="navigationBarColor">@android:color/transparent</item>
<item name="windowLightNavigationBar">true</item>
+ <item name="statusBarColor">@android:color/transparent</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
diff --git a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
index 1b65603..707d7b3 100644
--- a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
+++ b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
@@ -19,13 +19,22 @@
import com.google.caliper.BeforeExperiment;
import com.google.caliper.Param;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
public class NetworkStatsBenchmark {
- private static final String UNDERLYING_IFACE = "wlan0";
+ private static final String[] UNDERLYING_IFACES = {"wlan0", "rmnet0"};
private static final String TUN_IFACE = "tun0";
private static final int TUN_UID = 999999999;
@Param({"100", "1000"})
private int mSize;
+ /**
+ * Should not be more than the length of {@link #UNDERLYING_IFACES}.
+ */
+ @Param({"1", "2"})
+ private int mNumUnderlyingIfaces;
private NetworkStats mNetworkStats;
@BeforeExperiment
@@ -33,8 +42,10 @@
mNetworkStats = new NetworkStats(0, mSize + 2);
int uid = 0;
NetworkStats.Entry recycle = new NetworkStats.Entry();
+ final List<String> allIfaces = getAllIfacesForBenchmark(); // also contains TUN_IFACE.
+ final int totalIfaces = allIfaces.size();
for (int i = 0; i < mSize; i++) {
- recycle.iface = (i < mSize / 2) ? TUN_IFACE : UNDERLYING_IFACE;
+ recycle.iface = allIfaces.get(i % totalIfaces);
recycle.uid = uid;
recycle.set = i % 2;
recycle.tag = NetworkStats.TAG_NONE;
@@ -48,22 +59,39 @@
uid++;
}
}
- recycle.iface = UNDERLYING_IFACE;
- recycle.uid = TUN_UID;
- recycle.set = NetworkStats.SET_FOREGROUND;
- recycle.tag = NetworkStats.TAG_NONE;
- recycle.rxBytes = 90000 * mSize;
- recycle.rxPackets = 40 * mSize;
- recycle.txBytes = 180000 * mSize;
- recycle.txPackets = 1200 * mSize;
- recycle.operations = 0;
- mNetworkStats.addValues(recycle);
+
+ for (int i = 0; i < mNumUnderlyingIfaces; i++) {
+ recycle.iface = UNDERLYING_IFACES[i];
+ recycle.uid = TUN_UID;
+ recycle.set = NetworkStats.SET_FOREGROUND;
+ recycle.tag = NetworkStats.TAG_NONE;
+ recycle.rxBytes = 90000 * mSize;
+ recycle.rxPackets = 40 * mSize;
+ recycle.txBytes = 180000 * mSize;
+ recycle.txPackets = 1200 * mSize;
+ recycle.operations = 0;
+ mNetworkStats.addValues(recycle);
+ }
+ }
+
+ private String[] getVpnUnderlyingIfaces() {
+ return Arrays.copyOf(UNDERLYING_IFACES, mNumUnderlyingIfaces);
+ }
+
+ /**
+ * Same as {@link #getVpnUnderlyingIfaces}, but also contains {@link #TUN_IFACE}.
+ */
+ private List<String> getAllIfacesForBenchmark() {
+ List<String> ifaces = new ArrayList<>();
+ ifaces.add(TUN_IFACE);
+ ifaces.addAll(Arrays.asList(getVpnUnderlyingIfaces()));
+ return ifaces;
}
public void timeMigrateTun(int reps) {
for (int i = 0; i < reps; i++) {
NetworkStats stats = mNetworkStats.clone();
- stats.migrateTun(TUN_UID, TUN_IFACE, UNDERLYING_IFACE);
+ stats.migrateTun(TUN_UID, TUN_IFACE, getVpnUnderlyingIfaces());
}
}
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 37020fc..9272ea5 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -31,6 +31,13 @@
}
prebuilt_etc {
+ name: "privapp_whitelist_android.car.cluster",
+ sub_dir: "permissions",
+ src: "android.car.cluster.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "privapp_whitelist_android.car.usb.handler",
sub_dir: "permissions",
src: "android.car.usb.handler.xml",
diff --git a/data/etc/car/android.car.cluster.xml b/data/etc/car/android.car.cluster.xml
new file mode 100644
index 0000000..d7f29da
--- /dev/null
+++ b/data/etc/car/android.car.cluster.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="android.car.cluster">
+ <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/car/com.android.car.xml b/data/etc/car/com.android.car.xml
index f1797de..19548bc 100644
--- a/data/etc/car/com.android.car.xml
+++ b/data/etc/car/com.android.car.xml
@@ -24,6 +24,7 @@
<permission name="android.permission.PROVIDE_TRUST_AGENT"/>
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.REBOOT"/>
+ <permission name="android.permission.READ_LOGS"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index 6b26e8f..d36a826 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -25,6 +25,7 @@
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.PROVIDE_TRUST_AGENT"/>
<permission name="android.permission.REAL_GET_TASKS"/>
+ <permission name="android.permission.READ_LOGS"/>
<permission name="android.permission.REBOOT"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index dc3041f..820d82d 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -222,10 +222,10 @@
public final static int SUPPRESSIBLE_MEDIA = 5;
/**
* @hide
- * Denotes a usage for all other sounds not caught in SUPPRESSIBLE_NOTIFICATION,
+ * Denotes a usage for sounds not caught in SUPPRESSIBLE_NOTIFICATION,
* SUPPRESSIBLE_CALL,SUPPRESSIBLE_NEVER, SUPPRESSIBLE_ALARM or SUPPRESSIBLE_MEDIA.
- * This includes system, sonification and unknown sounds.
- * These will be muted when the Zen priority mode doesn't allow sytem sounds
+ * This includes sonification sounds.
+ * These will be muted when the Zen priority mode doesn't allow system sounds
* @see #SUPPRESSIBLE_USAGES
*/
public final static int SUPPRESSIBLE_SYSTEM = 6;
@@ -248,6 +248,7 @@
SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_EVENT, SUPPRESSIBLE_NOTIFICATION);
SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_ACCESSIBILITY, SUPPRESSIBLE_NEVER);
SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION, SUPPRESSIBLE_NEVER);
+ SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION_SIGNALLING, SUPPRESSIBLE_NEVER);
SUPPRESSIBLE_USAGES.put(USAGE_ALARM, SUPPRESSIBLE_ALARM);
SUPPRESSIBLE_USAGES.put(USAGE_MEDIA, SUPPRESSIBLE_MEDIA);
SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, SUPPRESSIBLE_MEDIA);
@@ -255,7 +256,6 @@
SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANT, SUPPRESSIBLE_MEDIA);
/** default volume assignment is STREAM_MUSIC, handle unknown usage as media */
SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN, SUPPRESSIBLE_MEDIA);
- SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION_SIGNALLING, SUPPRESSIBLE_SYSTEM);
SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_SONIFICATION, SUPPRESSIBLE_SYSTEM);
}
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index b5c4cca..8ee929e 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -38,6 +38,7 @@
import java.net.UnknownServiceException;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
/** @hide */
public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
@@ -83,6 +84,10 @@
private final static int HTTP_TEMP_REDIRECT = 307;
private final static int MAX_REDIRECTS = 20;
+ // The number of threads that are currently running disconnect() (possibly
+ // not yet holding the synchronized lock).
+ private final AtomicInteger mNumDisconnectingThreads = new AtomicInteger(0);
+
@UnsupportedAppUsage
public MediaHTTPConnection() {
CookieHandler cookieHandler = CookieHandler.getDefault();
@@ -155,19 +160,24 @@
@Override
@UnsupportedAppUsage
public void disconnect() {
- HttpURLConnection connectionToDisconnect = mConnection;
- // Call disconnect() before blocking for the lock in order to ensure that any
- // other thread that is blocked in readAt() will return quickly.
- if (connectionToDisconnect != null) {
- connectionToDisconnect.disconnect();
- }
- synchronized (this) {
- // It's unlikely but possible that while we were waiting to acquire the lock, another
- // thread concurrently started a new connection; if so, we're disconnecting that one
- // here, too.
- teardownConnection();
- mHeaders = null;
- mURL = null;
+ mNumDisconnectingThreads.incrementAndGet();
+ try {
+ HttpURLConnection connectionToDisconnect = mConnection;
+ // Call disconnect() before blocking for the lock in order to ensure that any
+ // other thread that is blocked in readAt() will return quickly.
+ if (connectionToDisconnect != null) {
+ connectionToDisconnect.disconnect();
+ }
+ synchronized (this) {
+ // It's possible that while we were waiting to acquire the lock, another thread
+ // concurrently started a new connection; if so, we're disconnecting that one
+ // here, too.
+ teardownConnection();
+ mHeaders = null;
+ mURL = null;
+ }
+ } finally {
+ mNumDisconnectingThreads.decrementAndGet();
}
}
@@ -224,11 +234,36 @@
boolean noProxy = isLocalHost(url);
while (true) {
+ // If another thread is concurrently disconnect()ing, there's a race
+ // between them and us. Therefore, we check mNumDisconnectingThreads shortly
+ // (not atomically) before & after writing mConnection. This guarantees that
+ // we won't "lose" a disconnect by creating a new connection that might
+ // miss the disconnect.
+ //
+ // Note that throwing an instanceof IOException is also what this thread
+ // would have done if another thread disconnect()ed the connection while
+ // this thread was blocked reading from that connection further down in this
+ // loop.
+ if (mNumDisconnectingThreads.get() > 0) {
+ throw new IOException("concurrently disconnecting");
+ }
if (noProxy) {
mConnection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
} else {
mConnection = (HttpURLConnection)url.openConnection();
}
+ // If another thread is concurrently disconnecting, throwing IOException will
+ // cause us to release the lock, giving the other thread a chance to acquire
+ // it. It also ensures that the catch block will run, which will tear down
+ // the connection even if the other thread happens to already be on its way
+ // out of disconnect().
+ if (mNumDisconnectingThreads.get() > 0) {
+ throw new IOException("concurrently disconnecting");
+ }
+ // If we get here without having thrown, we know that other threads
+ // will see our write to mConnection. Any disconnect() on that mConnection
+ // instance will cause our read from/write to that connection instance below
+ // to encounter an instanceof IOException.
mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS);
// handle redirects ourselves if we do not allow cross-domain redirect
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
index 7f23ba5..9b643ad 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
@@ -25,7 +25,6 @@
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.ShutterCallback;
import android.os.ConditionVariable;
-import android.os.Environment;
import android.os.Looper;
import android.test.ActivityInstrumentationTestCase;
import android.test.suitebuilder.annotation.LargeTest;
@@ -159,7 +158,7 @@
if (rawData != null) {
int rawDataLength = rawData.length;
File rawoutput = new File(
- Environment.getExternalStorageDirectory().toString(), "/test.bmp");
+ mContext.getExternalFilesDir(null).getPath(), "/test.bmp");
FileOutputStream outstream = new FileOutputStream(rawoutput);
outstream.write(rawData);
Log.v(TAG, "JpegPictureCallback rawDataLength = " + rawDataLength);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestHelper.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestHelper.java
index 84153d60..bd236a6 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestHelper.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestHelper.java
@@ -22,10 +22,11 @@
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
-import android.os.Environment;
import android.util.Log;
import android.view.SurfaceHolder;
+import androidx.test.InstrumentationRegistry;
+
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
@@ -70,7 +71,8 @@
try {
Log.v(TAG, "JPEG picture taken");
fos = new FileOutputStream(String.format("%s/%s/%s-%d.jpg",
- Environment.getExternalStorageDirectory(), CAMERA_STRESS_IMAGES_DIRECTORY,
+ InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getExternalFilesDir(null).getPath(), CAMERA_STRESS_IMAGES_DIRECTORY,
CAMERA_STRESS_IMAGES_PREFIX, System.currentTimeMillis()));
fos.write(data);
} catch (FileNotFoundException e) {
@@ -95,7 +97,8 @@
public void setupCameraTest() {
// Create the test images directory if it doesn't exist
File stressImagesDirectory = new File(String.format("%s/%s",
- Environment.getExternalStorageDirectory(), CAMERA_STRESS_IMAGES_DIRECTORY));
+ InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getExternalFilesDir(null).getPath(), CAMERA_STRESS_IMAGES_DIRECTORY));
if (!stressImagesDirectory.exists()) {
stressImagesDirectory.mkdir();
}
@@ -129,7 +132,8 @@
public void cleanupTestImages() {
try {
File stressImagesDirectory = new File(String.format("%s/%s",
- Environment.getExternalStorageDirectory(), CAMERA_STRESS_IMAGES_DIRECTORY));
+ InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getExternalFilesDir(null).getPath(), CAMERA_STRESS_IMAGES_DIRECTORY));
File[] stressImages = stressImagesDirectory.listFiles();
for (File f : stressImages) {
f.delete();
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
index 0340cec..0ae640d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
@@ -54,7 +54,6 @@
import android.media.ImageReader;
import android.media.ImageWriter;
import android.os.Build;
-import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.util.Pair;
@@ -63,6 +62,8 @@
import android.view.Surface;
import android.view.WindowManager;
+import androidx.test.InstrumentationRegistry;
+
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
@@ -128,7 +129,8 @@
private static final Location sTestLocation2 = new Location(LocationManager.NETWORK_PROVIDER);
protected static final String DEBUG_FILE_NAME_BASE =
- Environment.getExternalStorageDirectory().getPath();
+ InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getExternalFilesDir(null).getPath();
static {
sTestLocation0.setTime(1199145600L);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2SwitchPreviewTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2SwitchPreviewTest.java
index 11327ca..a26ee2d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2SwitchPreviewTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2SwitchPreviewTest.java
@@ -104,7 +104,6 @@
private static final double AE_COMPENSATION_ERROR_TOLERANCE = 0.2;
// 5 percent error margin for resulting metering regions
private static final float METERING_REGION_ERROR_PERCENT_DELTA = 0.05f;
- private final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath();
private static final boolean DEBUG_DUMP = Log.isLoggable(TAG, Log.DEBUG);
private static final int RECORDING_DURATION_MS = 3000;
@@ -137,10 +136,12 @@
private int mVideoFrameRate;
private Size mVideoSize;
private long mRecordingStartTime;
+ private String mVideoFilePath;
@Override
protected void setUp() throws Exception {
super.setUp();
+ mVideoFilePath = mContext.getExternalFilesDir(null).getPath();
}
@Override
@@ -371,9 +372,9 @@
}
// Configure preview and recording surfaces.
- mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
+ mOutMediaFileName = mVideoFilePath + "/test_video.mp4";
if (DEBUG_DUMP) {
- mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_"
+ mOutMediaFileName = mVideoFilePath + "/test_video_" + cameraId + "_"
+ videoSz.toString() + ".mp4";
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
index d1193de..74244b9 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
@@ -28,7 +28,6 @@
import java.util.List;
import android.hardware.Camera.Parameters;
-import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.test.ActivityInstrumentationTestCase2;
@@ -36,6 +35,8 @@
import android.util.Log;
import android.view.SurfaceHolder;
+import androidx.test.InstrumentationRegistry;
+
/**
* Junit / Instrumentation test case for the following camera APIs:
* - camera zoom
@@ -85,7 +86,8 @@
mCameraTestHelper = new CameraTestHelper();
File stressOutFile = new File(String.format("%s/%s",
- Environment.getExternalStorageDirectory(), CAMERA_STRESS_OUTPUT));
+ InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getExternalFilesDir(null).getPath(), CAMERA_STRESS_OUTPUT));
mOutput = new BufferedWriter(new FileWriter(stressOutFile, true));
mOutput.write(this.getName() + "\n");
}
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 7ee2f0a..8fe4fec 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -384,6 +384,9 @@
transaction->setCrop(surfaceControl, static_cast<const Rect&>(source));
transaction->setFrame(surfaceControl, static_cast<const Rect&>(destination));
transaction->setTransform(surfaceControl, transform);
+ bool transformToInverseDisplay = (NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY & transform) ==
+ NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+ transaction->setTransformToDisplayInverse(surfaceControl, transformToInverseDisplay);
}
void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* aSurfaceTransaction,
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index da3416b..1b27b52 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -638,7 +638,7 @@
final String docId = DocumentsContract.getDocumentId(documentUri);
try {
final Bundle out = new Bundle();
- final Uri uri = Uri.fromFile(getFileForDocId(docId));
+ final Uri uri = Uri.fromFile(getFileForDocId(docId, true));
out.putParcelable(DocumentsContract.EXTRA_URI, uri);
return out;
} catch (FileNotFoundException e) {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 4c72f48..0e91839 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -578,6 +578,8 @@
<string name="wifi_display_certification">Wireless display certification</string>
<!-- Setting Checkbox title whether to enable WiFi Verbose Logging. [CHAR LIMIT=40] -->
<string name="wifi_verbose_logging">Enable Wi\u2011Fi Verbose Logging</string>
+ <!-- Setting Checkbox title whether to disable WiFi Scan Throttling. [CHAR LIMIT=40] -->
+ <string name="wifi_scan_throttling">Wi\u2011Fi scan throttling</string>
<!-- Setting Checkbox title whether to always keep mobile data active. [CHAR LIMIT=80] -->
<string name="mobile_data_always_on">Mobile data always active</string>
<!-- Setting Checkbox title whether to enable hardware acceleration for tethering. [CHAR LIMIT=80] -->
@@ -633,6 +635,8 @@
<string name="wifi_display_certification_summary">Show options for wireless display certification</string>
<!-- Setting Checkbox summary whether to enable Wifi verbose Logging [CHAR LIMIT=80] -->
<string name="wifi_verbose_logging_summary">Increase Wi\u2011Fi logging level, show per SSID RSSI in Wi\u2011Fi Picker</string>
+ <!-- Setting Checkbox summary whether to disable Wifi scan throttling [CHAR LIMIT=NONE] -->
+ <string name="wifi_scan_throttling_summary">Reduces battery drain & improves network performance</string>
<!-- Label indicating network has been manually marked as metered -->
<string name="wifi_metered_label">Metered</string>
<!-- Label indicating network has been manually marked as unmetered -->
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index a245086..da139d7 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -206,8 +206,9 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
<application android:label="@string/app_label"
- android:defaultToDeviceProtectedStorage="true"
- android:directBootAware="true">
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:defaultToDeviceProtectedStorage="true"
+ android:directBootAware="true">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.android.shell"
@@ -232,7 +233,6 @@
<activity
android:name=".BugreportWarningActivity"
- android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
android:exported="false" />
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 1060c7b..e57a5de 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -99,6 +99,7 @@
import android.util.Pair;
import android.util.Patterns;
import android.util.SparseArray;
+import android.view.ContextThemeWrapper;
import android.view.IWindowManager;
import android.view.View;
import android.view.WindowManager;
@@ -1467,11 +1468,13 @@
void initialize(final Context context, BugreportInfo info) {
final String dialogTitle =
context.getString(R.string.bugreport_info_dialog_title, info.id);
+ final Context themedContext = new ContextThemeWrapper(
+ context, com.android.internal.R.style.Theme_DeviceDefault_DayNight);
// First initializes singleton.
if (mDialog == null) {
@SuppressLint("InflateParams")
// It's ok pass null ViewRoot on AlertDialogs.
- final View view = View.inflate(context, R.layout.dialog_bugreport_info, null);
+ final View view = View.inflate(themedContext, R.layout.dialog_bugreport_info, null);
mInfoName = (EditText) view.findViewById(R.id.name);
mInfoTitle = (EditText) view.findViewById(R.id.title);
@@ -1488,7 +1491,7 @@
}
});
- mDialog = new AlertDialog.Builder(context)
+ mDialog = new AlertDialog.Builder(themedContext)
.setView(view)
.setTitle(dialogTitle)
.setCancelable(true)
diff --git a/packages/SystemUI/res/drawable/corner_gesture_hint.xml b/packages/SystemUI/res/drawable/corner_gesture_hint.xml
deleted file mode 100644
index 3f4abb0..0000000
--- a/packages/SystemUI/res/drawable/corner_gesture_hint.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- Copyright (C) 2019 The Android Open Source Project
-
- 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="12dp"
- android:width="12dp"
- android:viewportWidth="12"
- android:viewportHeight="12">
-
- <path android:fillColor="#00000000"
- android:pathData="M 1.18 10.65 C 1.18 5.58 5.41 1.18 10.65 1.18"
- android:strokeColor="#000"
- android:strokeLineCap="round"
- android:strokeWidth="1.3" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_notifications_alert.xml b/packages/SystemUI/res/drawable/ic_notifications_alert.xml
index eb7b8ee..d53d698 100644
--- a/packages/SystemUI/res/drawable/ic_notifications_alert.xml
+++ b/packages/SystemUI/res/drawable/ic_notifications_alert.xml
@@ -14,11 +14,11 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
<path
- android:pathData="M7.58 4.08L6.15 2.65C3.75 4.48 2.17 7.3 2.03 10.5h2c.15-2.65 1.51-4.97 3.55-6.42zm12.39 6.42h2c-.15-3.2-1.73-6.02-4.12-7.85l-1.42 1.43c2.02 1.45 3.39 3.77 3.54 6.42zM18 11c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2v-5zm-6 11c.14 0 .27-.01.4-.04.65-.14 1.18-.58 1.44-1.18.1-.24.15-.5.15-.78h-4c.01 1.1.9 2 2.01 2z"
- android:fillColor="#FF000000"/>
-</vector>
+ android:fillColor="@android:color/white"
+ android:pathData="M18 17v-6c0-3.07-1.63-5.64-4.5-6.32V4c0-0.83-0.67-1.5-1.5-1.5s-1.5 0.67 -1.5 1.5v0.68C7.64 5.36 6 7.92 6 11v6H4v2h16v-2h-2zm-2 0H8v-6c0-2.48 1.51-4.5 4-4.5s4 2.02 4 4.5v6zm-6 3h4c0 1.1-0.9 2-2 2s-2-0.9-2-2zm12-9h-2c0-2.74-1.23-5.19-3.16-6.84l1.41-1.41C20.54 4.77 22 7.71 22 11zM5.75 2.75l1.41 1.41C5.23 5.81 4 8.26 4 11H2c0-3.29 1.46-6.23 3.75-8.25z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_notifications_silence.xml b/packages/SystemUI/res/drawable/ic_notifications_silence.xml
index ff136eb..a6cc81b 100644
--- a/packages/SystemUI/res/drawable/ic_notifications_silence.xml
+++ b/packages/SystemUI/res/drawable/ic_notifications_silence.xml
@@ -14,15 +14,11 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
<path
- android:pathData="M0 0h24v24H0z"
- />
- <path
- android:pathData="M20 18.69L7.84 6.14 5.27 3.49 4 4.76l2.8 2.8v.01c-.52.99-.8 2.16-.8 3.42v5l-2 2v1h13.73l2 2L21 19.72l-1-1.03zM12 22c1.11 0 2-.89 2-2h-4c0 1.11.89 2 2 2zm6-7.32V11c0-3.08-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68c-.15.03-.29.08-.42.12-.1.03-.2.07-.3.11h-.01c-.01 0-.01 0-.02.01-.23.09-.46.2-.68.31 0 0-.01 0-.01.01L18 14.68z"
- android:fillColor="#FF000000"
- />
-</vector>
+ android:fillColor="@android:color/white"
+ android:pathData="M12 22c1.1 0 2-0.9 2-2h-4c0 1.1 0.9 2 2 2zm4-6L2.81 2.81 1.39 4.22l4.85 4.85C6.09 9.68 6 10.33 6 11v6H4v2h12.17l3.61 3.61 1.41-1.41L16 16zm-8 1l0.01-6.16L14.17 17H8zm4-10.5c2.49 0 4 2.02 4 4.5v2.17l2 2V11c0-3.07-1.63-5.64-4.5-6.32V4c0-0.83-0.67-1.5-1.5-1.5s-1.5 0.67 -1.5 1.5v0.68c-0.78 0.18 -1.45 0.52 -2.04 0.95 L9.93 7.1c0.58-0.37 1.27-0.6 2.07-0.6z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_media_bt.xml b/packages/SystemUI/res/drawable/ic_volume_media_bt.xml
index 9f7744e..23cb206 100644
--- a/packages/SystemUI/res/drawable/ic_volume_media_bt.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_media_bt.xml
@@ -14,14 +14,12 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
android:tint="?android:attr/colorControlNormal">
-
<path
- android:fillColor="#FFFFFFFF"
- android:pathData="M9,3l0.01,10.55C8.41,13.21 7.73,13 7.01,13C4.79,13 3,14.79 3,17c0,2.21 1.79,4 4.01,4S11,19.21 11,17V7h4V3H9zM7.01,19c-1.1,0 -2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2C9.01,18.1 8.11,19 7.01,19zM21,12.43L17.57,9h-0.6v4.55l-2.75,-2.75l-0.85,0.85L16.73,15l-3.35,3.35l0.85,0.85l2.75,-2.75V21h0.6L21,17.57L18.42,15L21,12.43zM18.17,11.3l1.13,1.13l-1.13,1.13V11.3zM19.3,17.57l-1.13,1.13v-2.26L19.3,17.57z"/>
-
-</vector>
+ android:fillColor="@android:color/white"
+ android:pathData="M9 3l0.01 10.55c-0.6-0.34-1.28-0.55-2-0.55C4.79 13 3 14.79 3 17s1.79 4 4.01 4S11 19.21 11 17V7h4V3H9zm12 9.43L17.57 9h-0.6v4.55l-2.75-2.75-0.85 0.85 L16.73 15l-3.35 3.35 0.85 0.85 2.75-2.75V21h0.6L21 17.57 18.42 15 21 12.43zm-2.83-1.13l1.13 1.13-1.13 1.13V11.3zm1.13 6.27l-1.13 1.13v-2.26l1.13 1.13z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_media_bt_mute.xml b/packages/SystemUI/res/drawable/ic_volume_media_bt_mute.xml
index 12e0f2e..2469ddc 100644
--- a/packages/SystemUI/res/drawable/ic_volume_media_bt_mute.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_media_bt_mute.xml
@@ -14,14 +14,12 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0"
android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
android:tint="?android:attr/colorControlNormal">
-
<path
- android:fillColor="#FFFFFFFF"
- android:pathData="M9,6.17L9,3h6v4h-4v1.17L9,6.17zM19.42,15L22,17.57l-0.8,0.8l-6.78,-6.78l0.8,-0.8l2.75,2.75V9h0.6L22,12.43L19.42,15zM19.17,13.55l1.13,-1.13l-1.13,-1.13V13.55zM17.21,17.21l3.98,3.98l-1.41,1.41l-3.98,-3.98l-0.58,0.58l-0.85,-0.85l0.58,-0.58L11,13.83V17c0,2.21 -1.78,4 -3.99,4S3,19.21 3,17c0,-2.21 1.79,-4 4.01,-4c0.73,0 1.41,0.21 2,0.55l0,-1.72L1.39,4.22l1.41,-1.41l13.56,13.56L17.21,17.21zM9.01,17c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2c0,1.1 0.9,2 2,2S9.01,18.1 9.01,17z"/>
-
-</vector>
+ android:fillColor="@android:color/white"
+ android:pathData="M9 6.17V3h6v4h-4v1.17l-2-2zM19.42 15L22 17.57l-0.8 0.8 -6.78-6.78 0.8 -0.8 2.75 2.75V9h0.6L22 12.43 19.42 15zm-0.25-1.45l1.13-1.13-1.13-1.13v2.26zm-1.96 3.66l3.98 3.98-1.41 1.41-3.98-3.98-0.58 0.58 -0.85-0.85 0.58 -0.58L11 13.83V17c0 2.21-1.78 4-3.99 4S3 19.21 3 17s1.79-4 4.01-4c0.73 0 1.41 0.21 2 0.55v-1.72L1.39 4.22 2.8 2.81l13.56 13.56 0.85 0.84z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/rounded_corners.xml b/packages/SystemUI/res/layout/rounded_corners.xml
index 02651a2..b409c8f 100644
--- a/packages/SystemUI/res/layout/rounded_corners.xml
+++ b/packages/SystemUI/res/layout/rounded_corners.xml
@@ -18,36 +18,30 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <com.android.systemui.CornerHandleView
+ android:id="@+id/assist_hint_left"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_gravity="left|top"
+ android:visibility="gone"/>
+ <com.android.systemui.CornerHandleView
+ android:id="@+id/assist_hint_right"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_gravity="right|bottom"
+ android:visibility="gone"/>
<ImageView
android:id="@+id/left"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_gravity="left|top"
android:tint="#ff000000"
- android:src="@drawable/rounded" />
+ android:src="@drawable/rounded"/>
<ImageView
android:id="@+id/right"
android:layout_width="12dp"
android:layout_height="12dp"
android:tint="#ff000000"
android:layout_gravity="right|bottom"
- android:src="@drawable/rounded" />
- <ImageView
- android:id="@+id/assist_hint_left"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:padding="6dp"
- android:layout_gravity="left|top"
- android:src="@drawable/corner_gesture_hint"
- android:tint="#ffffffff"
- android:visibility="gone" />
- <ImageView
- android:id="@+id/assist_hint_right"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:padding="6dp"
- android:layout_gravity="right|bottom"
- android:src="@drawable/corner_gesture_hint"
- android:tint="#ffffffff"
- android:visibility="gone" />
+ android:src="@drawable/rounded"/>
</com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1987494..c04d28a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -38,7 +38,6 @@
<dimen name="navigation_handle_sample_horizontal_margin">10dp</dimen>
<dimen name="navigation_home_handle_width">72dp</dimen>
-
<!-- Size of the nav bar edge panels, should be greater to the
edge sensitivity + the drag threshold -->
<dimen name="navigation_edge_panel_width">70dp</dimen>
@@ -57,6 +56,9 @@
<!-- Luminance change threshold that allows applying new value if difference was exceeded -->
<item name="navigation_luminance_change_threshold" type="dimen" format="float">0.05</item>
+ <dimen name="floating_rotation_button_diameter">40dp</dimen>
+ <dimen name="floating_rotation_button_margin">4dp</dimen>
+
<!-- Height of notification icons in the status bar -->
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index de4c798..bce5c23 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -322,9 +322,6 @@
mCharging = pluggedIn;
mLevel = level;
updatePercentText();
- setContentDescription(
- getContext().getString(charging ? R.string.accessibility_battery_level_charging
- : R.string.accessibility_battery_level, level));
}
@Override
@@ -358,6 +355,9 @@
mBatteryController.getEstimatedTimeRemainingString((String estimate) -> {
if (estimate != null) {
mBatteryPercentView.setText(estimate);
+ setContentDescription(getContext().getString(
+ R.string.battery_low_percent_format_hybrid, mLevel, estimate));
+
} else {
setPercentTextAtCurrentLevel();
}
@@ -371,6 +371,9 @@
private void setPercentTextAtCurrentLevel() {
mBatteryPercentView.setText(
NumberFormat.getPercentInstance().format(mLevel / 100f));
+ setContentDescription(
+ getContext().getString(mCharging ? R.string.accessibility_battery_level_charging
+ : R.string.accessibility_battery_level, mLevel));
}
private void updateShowPercent() {
diff --git a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
new file mode 100644
index 0000000..528db5a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.animation.ArgbEvaluator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.os.SystemProperties;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+
+import com.android.settingslib.Utils;
+
+/**
+ * CornerHandleView draws an inset arc intended to be displayed within the screen decoration
+ * corners.
+ */
+public class CornerHandleView extends View {
+ private static final boolean ALLOW_TUNING = false;
+ private static final int ANGLE_DEGREES = 50;
+ public static final int MARGIN_DP = 11;
+ public static final int RADIUS_DP = 37;
+ public static final float STROKE_DP = 2.5f;
+
+ private Paint mPaint;
+ private int mLightColor;
+ private int mDarkColor;
+ private RectF mOval;
+
+
+ public CornerHandleView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeCap(Paint.Cap.ROUND);
+ mPaint.setStrokeWidth(getStrokePx());
+
+ final int dualToneDarkTheme = Utils.getThemeAttr(mContext,
+ R.attr.darkIconTheme);
+ final int dualToneLightTheme = Utils.getThemeAttr(mContext,
+ R.attr.lightIconTheme);
+ Context lightContext = new ContextThemeWrapper(mContext, dualToneLightTheme);
+ Context darkContext = new ContextThemeWrapper(mContext, dualToneDarkTheme);
+ mLightColor = Utils.getColorAttrDefaultColor(lightContext,
+ R.attr.singleToneColor);
+ mDarkColor = Utils.getColorAttrDefaultColor(darkContext,
+ R.attr.singleToneColor);
+
+ updateOval();
+ }
+
+ /**
+ * Receives an intensity from 0 (lightest) to 1 (darkest) and sets the handle color
+ * approriately. Intention is to match the home handle color.
+ */
+ public void updateDarkness(float darkIntensity) {
+ mPaint.setColor((int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
+ mLightColor,
+ mDarkColor));
+ invalidate();
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (ALLOW_TUNING) {
+ mPaint.setStrokeWidth(getStrokePx());
+ updateOval();
+ }
+
+ canvas.drawArc(mOval, 180 + ((90 - getAngle()) / 2), getAngle(), false,
+ mPaint);
+ }
+
+ // TODO(b/133834204): Remove tweaking of corner handles
+ private static float convertDpToPixel(float dp, Context context) {
+ return dp * ((float) context.getResources().getDisplayMetrics().densityDpi
+ / DisplayMetrics.DENSITY_DEFAULT);
+ }
+
+ private void updateOval() {
+ mOval = new RectF(getMarginPx() - (getStrokePx() / 2.f),
+ getMarginPx() - (getStrokePx() / 2.f),
+ getMarginPx() + (2 * (getRadiusPx()) + (getStrokePx() / 2.f)),
+ getMarginPx() + 2 * getRadiusPx() + (getStrokePx() / 2.f));
+ }
+
+ private int getAngle() {
+ if (ALLOW_TUNING) {
+ return SystemProperties.getInt("CORNER_HANDLE_ANGLE_DEGREES", ANGLE_DEGREES);
+ } else {
+ return ANGLE_DEGREES;
+ }
+ }
+
+ private int getMarginPx() {
+ if (ALLOW_TUNING) {
+ return SystemProperties.getInt("CORNER_HANDLE_MARGIN_PX",
+ (int) convertDpToPixel(MARGIN_DP, getContext()));
+ } else {
+ return (int) convertDpToPixel(MARGIN_DP, getContext());
+ }
+ }
+
+ private int getRadiusPx() {
+ if (ALLOW_TUNING) {
+ return SystemProperties.getInt("CORNER_HANDLE_RADIUS_PX",
+ (int) convertDpToPixel(RADIUS_DP, getContext()));
+ } else {
+ return (int) convertDpToPixel(RADIUS_DP, getContext());
+ }
+ }
+
+ private int getStrokePx() {
+ if (ALLOW_TUNING) {
+ return SystemProperties.getInt("CORNER_HANDLE_STROKE_PX",
+ (int) convertDpToPixel(STROKE_DP, getContext()));
+ } else {
+ return (int) convertDpToPixel(STROKE_DP, getContext());
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 390a9e6..4aaf85a 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -60,6 +60,12 @@
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.OvershootInterpolator;
+import android.view.animation.PathInterpolator;
+import android.view.animation.ScaleAnimation;
+import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -72,6 +78,7 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.SecureSetting;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
+import com.android.systemui.statusbar.phone.NavigationBarTransitions;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.tuner.TunablePadding;
import com.android.systemui.tuner.TunerService;
@@ -85,7 +92,8 @@
* An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
* for antialiasing and emulation purposes.
*/
-public class ScreenDecorations extends SystemUI implements Tunable {
+public class ScreenDecorations extends SystemUI implements Tunable,
+ NavigationBarTransitions.DarkIntensityListener {
private static final boolean DEBUG = false;
private static final String TAG = "ScreenDecorations";
@@ -93,13 +101,17 @@
public static final String PADDING = "sysui_rounded_content_padding";
private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
+ private static final boolean VERBOSE = false;
private DisplayManager mDisplayManager;
private DisplayManager.DisplayListener mDisplayListener;
- @VisibleForTesting protected int mRoundedDefault;
- @VisibleForTesting protected int mRoundedDefaultTop;
- @VisibleForTesting protected int mRoundedDefaultBottom;
+ @VisibleForTesting
+ protected int mRoundedDefault;
+ @VisibleForTesting
+ protected int mRoundedDefaultTop;
+ @VisibleForTesting
+ protected int mRoundedDefaultBottom;
private View mOverlay;
private View mBottomOverlay;
private float mDensity;
@@ -111,6 +123,8 @@
private SecureSetting mColorInversionSetting;
private boolean mPendingRotationChange;
private Handler mHandler;
+ private boolean mAssistHintBlocked = false;
+ private boolean mIsReceivingNavBarColor = false;
/**
* Converts a set of {@link Rect}s into a {@link Region}
@@ -137,15 +151,32 @@
putComponent(ScreenDecorations.class, this);
}
- private void fade(View view, boolean fadeIn) {
+ private void fade(View view, boolean fadeIn, boolean isLeft) {
if (fadeIn) {
view.animate().cancel();
- view.setAlpha(0f);
+ view.setAlpha(1f);
view.setVisibility(View.VISIBLE);
- view.animate().alpha(1f);
+
+ AnimationSet anim = new AnimationSet(true);
+ anim.setDuration(900);
+
+ Animation scaleAnimation = new ScaleAnimation(2f, 1f, 2f, 1f,
+ ScaleAnimation.RELATIVE_TO_SELF, 0.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
+ anim.addAnimation(scaleAnimation);
+ anim.setInterpolator(new PathInterpolator(0.02f, 0.44f, 0.67f, 1.00f));
+
+ Animation translateAnimation = new TranslateAnimation(
+ TranslateAnimation.RELATIVE_TO_SELF, isLeft ? -0.2f : 0.2f,
+ TranslateAnimation.RELATIVE_TO_SELF,
+ 0f,
+ TranslateAnimation.RELATIVE_TO_SELF, 0.2f, TranslateAnimation.RELATIVE_TO_SELF,
+ 0f);
+ anim.addAnimation(translateAnimation);
+ anim.setInterpolator(new OvershootInterpolator());
+ view.startAnimation(anim);
} else {
view.animate().cancel();
- view.animate().alpha(0f).withEndAction(() -> view.setVisibility(View.INVISIBLE));
+ view.animate().setDuration(400).alpha(0f);
}
}
@@ -161,35 +192,59 @@
return;
}
+ if (mAssistHintBlocked && visible) {
+ if (VERBOSE) {
+ Log.v(TAG, "Assist hint blocked, cannot make it visible");
+ }
+ return;
+ }
+
if (mAssistHintVisible != visible) {
mAssistHintVisible = visible;
- View assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left);
- View assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right);
- View assistHintBottomLeft = mBottomOverlay.findViewById(R.id.assist_hint_left);
- View assistHintBottomRight = mBottomOverlay.findViewById(R.id.assist_hint_right);
+ CornerHandleView assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left);
+ CornerHandleView assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right);
+ CornerHandleView assistHintBottomLeft = mBottomOverlay.findViewById(
+ R.id.assist_hint_left);
+ CornerHandleView assistHintBottomRight = mBottomOverlay.findViewById(
+ R.id.assist_hint_right);
switch (mRotation) {
case RotationUtils.ROTATION_NONE:
- fade(assistHintBottomLeft, mAssistHintVisible);
- fade(assistHintBottomRight, mAssistHintVisible);
+ fade(assistHintBottomLeft, mAssistHintVisible, /* isLeft = */ true);
+ fade(assistHintBottomRight, mAssistHintVisible, /* isLeft = */ false);
break;
case RotationUtils.ROTATION_LANDSCAPE:
- fade(assistHintTopRight, mAssistHintVisible);
- fade(assistHintBottomRight, mAssistHintVisible);
+ fade(assistHintTopRight, mAssistHintVisible, /* isLeft = */ true);
+ fade(assistHintBottomRight, mAssistHintVisible, /* isLeft = */ false);
break;
case RotationUtils.ROTATION_SEASCAPE:
- fade(assistHintTopLeft, mAssistHintVisible);
- fade(assistHintBottomLeft, mAssistHintVisible);
+ fade(assistHintTopLeft, mAssistHintVisible, /* isLeft = */ false);
+ fade(assistHintBottomLeft, mAssistHintVisible, /* isLeft = */ true);
break;
case RotationUtils.ROTATION_UPSIDE_DOWN:
- fade(assistHintTopLeft, mAssistHintVisible);
- fade(assistHintTopRight, mAssistHintVisible);
+ fade(assistHintTopLeft, mAssistHintVisible, /* isLeft = */ false);
+ fade(assistHintTopRight, mAssistHintVisible, /* isLeft = */ true);
break;
}
}
}
+ /**
+ * Prevents the assist hint from becoming visible even if `mAssistHintVisible` is true.
+ */
+ public void setAssistHintBlocked(boolean blocked) {
+ if (!mHandler.getLooper().isCurrentThread()) {
+ mHandler.post(() -> setAssistHintBlocked(blocked));
+ return;
+ }
+
+ mAssistHintBlocked = blocked;
+ if (mAssistHintVisible && mAssistHintBlocked) {
+ setAssistHintVisible(false);
+ }
+ }
+
@VisibleForTesting
Handler startHandlerThread() {
HandlerThread thread = new HandlerThread("ScreenDecorations");
@@ -253,12 +308,12 @@
.inflate(R.layout.rounded_corners, null);
mCutoutTop = new DisplayCutoutView(mContext, true,
this::updateWindowVisibilities, this);
- ((ViewGroup)mOverlay).addView(mCutoutTop);
+ ((ViewGroup) mOverlay).addView(mCutoutTop);
mBottomOverlay = LayoutInflater.from(mContext)
.inflate(R.layout.rounded_corners, null);
mCutoutBottom = new DisplayCutoutView(mContext, false,
this::updateWindowVisibilities, this);
- ((ViewGroup)mBottomOverlay).addView(mCutoutBottom);
+ ((ViewGroup) mBottomOverlay).addView(mCutoutBottom);
mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
mOverlay.setAlpha(0);
@@ -378,10 +433,26 @@
if (mOverlay != null) {
updateLayoutParams();
updateViews();
+ if (mAssistHintVisible) {
+ // If assist handles are visible, hide them without animation and then make them
+ // show once again (with corrected rotation).
+ hideAssistHandles();
+ setAssistHintVisible(true);
+ }
}
}
}
+ private void hideAssistHandles() {
+ if (mOverlay != null && mBottomOverlay != null) {
+ mOverlay.findViewById(R.id.assist_hint_left).setVisibility(View.GONE);
+ mOverlay.findViewById(R.id.assist_hint_right).setVisibility(View.GONE);
+ mBottomOverlay.findViewById(R.id.assist_hint_left).setVisibility(View.GONE);
+ mBottomOverlay.findViewById(R.id.assist_hint_right).setVisibility(View.GONE);
+ mAssistHintVisible = false;
+ }
+ }
+
private void updateRoundedCornerRadii() {
final int newRoundedDefault = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.rounded_corner_radius);
@@ -416,7 +487,7 @@
} else if (mRotation == RotationUtils.ROTATION_LANDSCAPE) {
updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0);
updateView(topRight, Gravity.BOTTOM | Gravity.LEFT, 270);
- updateView(bottomLeft, Gravity.TOP | Gravity.RIGHT, 90);;
+ updateView(bottomLeft, Gravity.TOP | Gravity.RIGHT, 90);
updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
} else if (mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
updateView(topLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
@@ -477,7 +548,7 @@
}
private void updateView(View v, int gravity, int rotation) {
- ((FrameLayout.LayoutParams)v.getLayoutParams()).gravity = gravity;
+ ((FrameLayout.LayoutParams) v.getLayoutParams()).gravity = gravity;
v.setRotation(rotation);
}
@@ -613,6 +684,10 @@
setSize(mOverlay.findViewById(R.id.right), sizeTop);
setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom);
setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom);
+ setSize(mOverlay.findViewById(R.id.assist_hint_left), sizeTop * 2);
+ setSize(mOverlay.findViewById(R.id.assist_hint_right), sizeTop * 2);
+ setSize(mBottomOverlay.findViewById(R.id.assist_hint_left), sizeBottom * 2);
+ setSize(mBottomOverlay.findViewById(R.id.assist_hint_right), sizeBottom * 2);
}
});
}
@@ -624,6 +699,27 @@
view.setLayoutParams(params);
}
+ @Override
+ public void onDarkIntensity(float darkIntensity) {
+ if (mOverlay != null) {
+ CornerHandleView assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left);
+ CornerHandleView assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right);
+
+ assistHintTopLeft.updateDarkness(darkIntensity);
+ assistHintTopRight.updateDarkness(darkIntensity);
+ }
+
+ if (mBottomOverlay != null) {
+ CornerHandleView assistHintBottomLeft = mBottomOverlay.findViewById(
+ R.id.assist_hint_left);
+ CornerHandleView assistHintBottomRight = mBottomOverlay.findViewById(
+ R.id.assist_hint_right);
+
+ assistHintBottomLeft.updateDarkness(darkIntensity);
+ assistHintBottomRight.updateDarkness(darkIntensity);
+ }
+ }
+
@VisibleForTesting
static class TunablePaddingTagListener implements FragmentListener {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
index 0203d9e..ff508ad 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
@@ -24,9 +24,13 @@
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.provider.DeviceConfig;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.Dependency;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.SysUiServiceProvider;
@@ -46,6 +50,7 @@
public final class AssistHandleBehaviorController implements AssistHandleCallbacks {
private static final String TAG = "AssistHandleBehavior";
+
private static final boolean IS_DEBUG_DEVICE =
Build.TYPE.toLowerCase(Locale.ROOT).contains("debug")
|| Build.TYPE.toLowerCase(Locale.ROOT).equals("eng");
@@ -87,6 +92,19 @@
Dependency.get(NavigationModeController.class)
.addListener(this::handleNavigationModeChange));
+ setBehavior(DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE,
+ mCurrentBehavior.toString()));
+ DeviceConfig.addOnPropertyChangedListener(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ mHandler::post,
+ (namespace, name, value) -> {
+ if (SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE.equals(name)) {
+ setBehavior(value);
+ }
+ });
+
if (IS_DEBUG_DEVICE) {
context.registerReceiver(new BroadcastReceiver() {
@Override
@@ -136,13 +154,29 @@
mCurrentBehavior = behavior;
}
- private static long getShownFrequencyThreshold() {
- return SystemProperties.getLong(
- SHOWN_FREQUENCY_THRESHOLD_KEY, DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS);
+ private void setBehavior(@Nullable String behavior) {
+ try {
+ setBehavior(AssistHandleBehavior.valueOf(behavior));
+ } catch (IllegalArgumentException | NullPointerException e) {
+ Log.e(TAG, "Invalid behavior: " + behavior, e);
+ }
}
- private static long getShowAndGoDuration() {
- return SystemProperties.getLong(SHOW_AND_GO_DURATION_KEY, DEFAULT_SHOW_AND_GO_DURATION_MS);
+ private long getShownFrequencyThreshold() {
+ long configValue = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS,
+ DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS);
+ return SystemProperties.getLong(
+ SHOWN_FREQUENCY_THRESHOLD_KEY, configValue);
+ }
+
+ private long getShowAndGoDuration() {
+ long configValue = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS,
+ DEFAULT_SHOW_AND_GO_DURATION_MS);
+ return SystemProperties.getLong(SHOW_AND_GO_DURATION_KEY, configValue);
}
private void maybeShowHandles(boolean ignoreThreshold) {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java
index e89e93c..05e504c 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java
@@ -16,17 +16,15 @@
package com.android.systemui.assist;
-import android.app.StatusBarManager;
import android.content.Context;
import androidx.annotation.Nullable;
import com.android.systemui.Dependency;
-import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.phone.NavigationBarFragment;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.system.QuickStepContract;
/**
* Assistant Handle behavior that makes Assistant handles show/hide when the home handle is
@@ -34,47 +32,68 @@
*/
final class AssistHandleLikeHomeBehavior implements BehaviorController {
- private final CommandQueue.Callbacks mCallbacks = new CommandQueue.Callbacks() {
+ private final StatusBarStateController.StateListener mStatusBarStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ handleDozingChanged(isDozing);
+ }
+ };
+ private final OverviewProxyService.OverviewProxyListener mOverviewProxyListener =
+ new OverviewProxyService.OverviewProxyListener() {
@Override
- public void setWindowState(int displayId, int window, int state) {
- if (mNavBarDisplayId == displayId
- && window == StatusBarManager.WINDOW_NAVIGATION_BAR) {
- handleWindowStateChanged(state);
- }
+ public void onSystemUiStateChanged(int sysuiStateFlags) {
+ handleSystemUiStateChange(sysuiStateFlags);
}
};
+ private final StatusBarStateController mStatusBarStateController;
+ private final OverviewProxyService mOverviewProxyService;
- private CommandQueue mCommandQueue;
- private int mNavBarDisplayId;
- private boolean mIsNavBarWindowVisible;
+ private boolean mIsDozing;
+ private boolean mIsHomeHandleHiding;
@Nullable private AssistHandleCallbacks mAssistHandleCallbacks;
+ AssistHandleLikeHomeBehavior() {
+ mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+ mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+ }
+
@Override
public void onModeActivated(Context context, AssistHandleCallbacks callbacks) {
mAssistHandleCallbacks = callbacks;
- NavigationBarFragment navigationBarFragment =
- Dependency.get(NavigationBarController.class).getDefaultNavigationBarFragment();
- mNavBarDisplayId = navigationBarFragment.mDisplayId;
- mIsNavBarWindowVisible = navigationBarFragment.isNavBarWindowVisible();
- mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
- mCommandQueue.addCallback(mCallbacks);
+ mIsDozing = mStatusBarStateController.isDozing();
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ mOverviewProxyService.addCallback(mOverviewProxyListener);
callbackForCurrentState();
}
@Override
public void onModeDeactivated() {
mAssistHandleCallbacks = null;
- mCommandQueue.removeCallback(mCallbacks);
+ mOverviewProxyService.removeCallback(mOverviewProxyListener);
}
- private void handleWindowStateChanged(int state) {
- boolean newVisibility = state == StatusBarManager.WINDOW_STATE_SHOWING;
- if (mIsNavBarWindowVisible == newVisibility) {
+ private static boolean isHomeHandleHiding(int sysuiStateFlags) {
+ return (sysuiStateFlags & QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN) != 0;
+ }
+
+ private void handleDozingChanged(boolean isDozing) {
+ if (mIsDozing == isDozing) {
return;
}
- mIsNavBarWindowVisible = newVisibility;
+ mIsDozing = isDozing;
+ callbackForCurrentState();
+ }
+
+ private void handleSystemUiStateChange(int sysuiStateFlags) {
+ boolean isHomeHandleHiding = isHomeHandleHiding(sysuiStateFlags);
+ if (mIsHomeHandleHiding == isHomeHandleHiding) {
+ return;
+ }
+
+ mIsHomeHandleHiding = isHomeHandleHiding;
callbackForCurrentState();
}
@@ -83,10 +102,10 @@
return;
}
- if (mIsNavBarWindowVisible) {
- mAssistHandleCallbacks.showAndStay();
- } else {
+ if (mIsHomeHandleHiding || mIsDozing) {
mAssistHandleCallbacks.hide();
+ } else {
+ mAssistHandleCallbacks.showAndStay();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 4a5e0e8..93e4886 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -16,22 +16,19 @@
package com.android.systemui.assist;
+import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
-import android.graphics.Rect;
-import android.view.View;
-import android.view.WindowManager;
import androidx.annotation.Nullable;
import com.android.systemui.Dependency;
-import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
/**
@@ -65,53 +62,45 @@
handleTaskStackTopChanged(taskId);
}
};
- private final CommandQueue.Callbacks mCallbacks = new CommandQueue.Callbacks() {
- @Override
- public void setSystemUiVisibility(int displayId, int vis,
- int fullscreenStackVis, int dockedStackVis, int mask,
- Rect fullscreenStackBounds, Rect dockedStackBounds,
- boolean navbarColorManagedByIme) {
- if (mStatusBarDisplayId == displayId) {
- handleSystemUiVisibilityChange(vis, mask);
- }
- }
- };
private final OverviewProxyService.OverviewProxyListener mOverviewProxyListener =
new OverviewProxyService.OverviewProxyListener() {
@Override
public void onOverviewShown(boolean fromHome) {
handleOverviewShown();
}
+
+ @Override
+ public void onSystemUiStateChanged(int sysuiStateFlags) {
+ handleSystemUiStateChanged(sysuiStateFlags);
+ }
};
- private StatusBarStateController mStatusBarStateController;
- private ActivityManagerWrapper mActivityManagerWrapper;
- private OverviewProxyService mOverviewProxyService;
- private int mStatusBarDisplayId;
- private CommandQueue mCommandQueue;
+ private final StatusBarStateController mStatusBarStateController;
+ private final ActivityManagerWrapper mActivityManagerWrapper;
+ private final OverviewProxyService mOverviewProxyService;
+
private boolean mOnLockscreen;
private boolean mIsDozing;
private int mRunningTaskId;
- private boolean mIsImmersive;
+ private boolean mIsNavBarHidden;
@Nullable private AssistHandleCallbacks mAssistHandleCallbacks;
+ AssistHandleReminderExpBehavior() {
+ mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+ mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
+ mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+ }
+
@Override
public void onModeActivated(Context context, AssistHandleCallbacks callbacks) {
mAssistHandleCallbacks = callbacks;
- mStatusBarStateController = Dependency.get(StatusBarStateController.class);
mOnLockscreen = onLockscreen(mStatusBarStateController.getState());
mIsDozing = mStatusBarStateController.isDozing();
mStatusBarStateController.addCallback(mStatusBarStateListener);
- mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
- mRunningTaskId = mActivityManagerWrapper.getRunningTask().taskId;
+ ActivityManager.RunningTaskInfo runningTaskInfo = mActivityManagerWrapper.getRunningTask();
+ mRunningTaskId = runningTaskInfo == null ? 0 : runningTaskInfo.taskId;
mActivityManagerWrapper.registerTaskStackListener(mTaskStackChangeListener);
- mStatusBarDisplayId =
- ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
- .getDefaultDisplay().getDisplayId();
- mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
- mCommandQueue.addCallback(mCallbacks);
- mOverviewProxyService = Dependency.get(OverviewProxyService.class);
mOverviewProxyService.addCallback(mOverviewProxyListener);
callbackForCurrentState();
}
@@ -121,10 +110,13 @@
mAssistHandleCallbacks = null;
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackChangeListener);
- mCommandQueue.removeCallback(mCallbacks);
mOverviewProxyService.removeCallback(mOverviewProxyListener);
}
+ private static boolean isNavBarHidden(int sysuiStateFlags) {
+ return (sysuiStateFlags & QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN) != 0;
+ }
+
private void handleStatusBarStateChanged(int newState) {
boolean onLockscreen = onLockscreen(newState);
if (mOnLockscreen == onLockscreen) {
@@ -153,13 +145,13 @@
callbackForCurrentState();
}
- private void handleSystemUiVisibilityChange(int vis, int mask) {
- boolean isImmersive = isImmersive(vis, mask);
- if (mIsImmersive == isImmersive) {
+ private void handleSystemUiStateChanged(int sysuiStateFlags) {
+ boolean isNavBarHidden = isNavBarHidden(sysuiStateFlags);
+ if (mIsNavBarHidden == isNavBarHidden) {
return;
}
- mIsImmersive = isImmersive;
+ mIsNavBarHidden = isNavBarHidden;
callbackForCurrentState();
}
@@ -172,17 +164,12 @@
|| statusBarState == StatusBarState.SHADE_LOCKED;
}
- private boolean isImmersive(int vis, int mask) {
- return ((vis & mask)
- & (View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)) != 0;
- }
-
private void callbackForCurrentState() {
if (mAssistHandleCallbacks == null) {
return;
}
- if (mIsDozing || mIsImmersive) {
+ if (mIsDozing || mIsNavBarHidden) {
mAssistHandleCallbacks.hide();
} else if (mOnLockscreen) {
mAssistHandleCallbacks.showAndStay();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 35b8d203..d4c7366 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -56,6 +56,8 @@
import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
+import com.android.systemui.util.wakelock.SettableWakeLock;
+import com.android.systemui.util.wakelock.WakeLock;
import java.util.Date;
import java.util.HashSet;
@@ -101,6 +103,8 @@
private final Handler mHandler;
private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
private final HashSet<Integer> mMediaInvisibleStates;
+ private final Object mMediaToken = new Object();
+ private SettableWakeLock mMediaWakeLock;
private ZenModeController mZenModeController;
private String mDatePattern;
private DateFormat mDateFormat;
@@ -114,7 +118,8 @@
private PendingIntent mPendingIntent;
protected NotificationMediaManager mMediaManager;
private StatusBarStateController mStatusBarStateController;
- protected MediaMetadata mMediaMetaData;
+ private CharSequence mMediaTitle;
+ private CharSequence mMediaArtist;
protected boolean mDozing;
private boolean mMediaIsVisible;
@@ -218,24 +223,18 @@
}
protected boolean needsMediaLocked() {
- return mMediaMetaData != null && mMediaIsVisible && mDozing;
+ return !TextUtils.isEmpty(mMediaTitle) && mMediaIsVisible && mDozing;
}
protected void addMediaLocked(ListBuilder listBuilder) {
- if (mMediaMetaData == null) {
+ if (TextUtils.isEmpty(mMediaTitle)) {
return;
}
+ listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(mMediaTitle));
- CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE);
- if (TextUtils.isEmpty(title)) {
- title = getContext().getResources().getString(R.string.music_controls_no_title);
- }
- listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(title));
-
- CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST);
- if (!TextUtils.isEmpty(album)) {
+ if (!TextUtils.isEmpty(mMediaArtist)) {
RowBuilder albumBuilder = new RowBuilder(mMediaUri);
- albumBuilder.setTitle(album);
+ albumBuilder.setTitle(mMediaArtist);
Icon mediaIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
IconCompat mediaIconCompat = mediaIcon == null ? null
@@ -306,6 +305,8 @@
mZenModeController.addCallback(this);
mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern);
mPendingIntent = PendingIntent.getActivity(getContext(), 0, new Intent(), 0);
+ mMediaWakeLock = new SettableWakeLock(WakeLock.createPartial(getContext(), "media"),
+ "media");
KeyguardSliceProvider.sInstance = this;
registerClockUpdate();
updateClockLocked();
@@ -425,12 +426,42 @@
public void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state) {
synchronized (this) {
boolean nextVisible = !mMediaInvisibleStates.contains(state);
- if (nextVisible == mMediaIsVisible && metadata == mMediaMetaData) {
- return;
+ mHandler.removeCallbacksAndMessages(mMediaToken);
+ if (mMediaIsVisible && !nextVisible) {
+ // We need to delay this event for a few millis when stopping to avoid jank in the
+ // animation. The media app might not send its update when buffering, and the slice
+ // would end up without a header for 0.5 second.
+ mMediaWakeLock.setAcquired(true);
+ mHandler.postDelayed(() -> {
+ updateMediaStateLocked(metadata, state);
+ mMediaWakeLock.setAcquired(false);
+ }, mMediaToken, 2000);
+ } else {
+ mMediaWakeLock.setAcquired(false);
+ updateMediaStateLocked(metadata, state);
}
- mMediaMetaData = metadata;
- mMediaIsVisible = nextVisible;
}
+ }
+
+ private void updateMediaStateLocked(MediaMetadata metadata, @PlaybackState.State int state) {
+ boolean nextVisible = !mMediaInvisibleStates.contains(state);
+ CharSequence title = null;
+ if (metadata != null) {
+ title = metadata.getText(MediaMetadata.METADATA_KEY_TITLE);
+ if (TextUtils.isEmpty(title)) {
+ title = getContext().getResources().getString(R.string.music_controls_no_title);
+ }
+ }
+ CharSequence artist = metadata == null ? null : metadata.getText(
+ MediaMetadata.METADATA_KEY_ARTIST);
+
+ if (nextVisible == mMediaIsVisible && TextUtils.equals(title, mMediaTitle)
+ && TextUtils.equals(artist, mMediaArtist)) {
+ return;
+ }
+ mMediaTitle = title;
+ mMediaArtist = artist;
+ mMediaIsVisible = nextVisible;
notifyChange();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index c65e90e..7c193b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -65,7 +65,7 @@
private val mDozeParameters: DozeParameters;
var willWakeUp = false
set(value) {
- if (value && mDozeAmount != 0.0f) {
+ if (!value || mDozeAmount != 0.0f) {
field = value
}
}
@@ -228,7 +228,7 @@
// if we animate, we see the shelf briefly visible. Instead we fully animate
// the notification and its background out
animate = false
- } else {
+ } else if (!mWakingUp && !willWakeUp){
entry.setAmbientGoingAway(true)
mEntrySetToClearWhenFinished.add(entry)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index dbc9a8b..d625b31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -648,6 +648,9 @@
if (!getShowingLayout().isDimmable()) {
return false;
}
+ if (showingAmbientPulsing()) {
+ return false;
+ }
return super.isDimmable();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index ecbb216..d911e1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.row;
+import static android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE;
+
import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
import android.animation.Animator;
@@ -29,6 +31,7 @@
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
+import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.view.LayoutInflater;
@@ -255,9 +258,13 @@
mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
mLeftMenuItems.clear();
mRightMenuItems.clear();
+
+ boolean showSnooze = Settings.Secure.getInt(mContext.getContentResolver(),
+ SHOW_NOTIFICATION_SNOOZE, 0) == 1;
+
// Construct the menu items based on the notification
- if (!isForeground) {
- // Only show snooze for non-foreground notifications
+ if (!isForeground && showSnooze) {
+ // Only show snooze for non-foreground notifications, and if the setting is on
mSnoozeItem = createSnoozeItem(mContext);
}
mAppOpsItem = createAppOpsItem(mContext);
@@ -268,7 +275,7 @@
}
if (!mIsUsingBidirectionalSwipe) {
- if (!isForeground) {
+ if (!isForeground && showSnooze) {
mRightMenuItems.add(mSnoozeItem);
}
mRightMenuItems.add(mInfoItem);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 91c43a1..ee2dacd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -25,6 +25,7 @@
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
+import android.metrics.LogMaker;
import android.os.Handler;
import android.text.format.DateUtils;
import android.util.Log;
@@ -35,6 +36,9 @@
import android.widget.TextView;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.TransformableView;
@@ -62,8 +66,11 @@
private NotificationMediaManager mMediaManager;
private View mSeekBarView;
private Context mContext;
+ private MetricsLogger mMetricsLogger;
- private SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBarChangeListener() {
+ @VisibleForTesting
+ protected SeekBar.OnSeekBarChangeListener mSeekListener =
+ new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
@@ -76,6 +83,7 @@
public void onStopTrackingTouch(SeekBar seekBar) {
if (mMediaController != null && canSeekMedia()) {
mMediaController.getTransportControls().seekTo(mSeekBar.getProgress());
+ mMetricsLogger.write(newLog(MetricsEvent.TYPE_UPDATE));
}
}
};
@@ -93,7 +101,8 @@
// Update the UI once, in case playback info changed while we were paused
mUpdatePlaybackUi.run();
clearTimer();
- } else if (mSeekBarTimer == null) {
+ } else if (mSeekBarTimer == null && mSeekBarView != null
+ && mSeekBarView.getVisibility() != View.GONE) {
startTimer();
}
}
@@ -104,6 +113,7 @@
super(ctx, view, row);
mContext = ctx;
mMediaManager = Dependency.get(NotificationMediaManager.class);
+ mMetricsLogger = Dependency.get(MetricsLogger.class);
}
private void resolveViews() {
@@ -121,11 +131,13 @@
}
// Check for existing media controller and clean up / create as necessary
+ boolean controllerUpdated = false;
if (mMediaController == null || !mMediaController.getSessionToken().equals(token)) {
if (mMediaController != null) {
mMediaController.unregisterCallback(mMediaCallback);
}
mMediaController = new MediaController(mContext, token);
+ controllerUpdated = true;
}
if (mMediaController.getMetadata() != null) {
@@ -134,14 +146,21 @@
if (duration <= 0) {
// Don't include the seekbar if this is a livestream
Log.d(TAG, "removing seekbar");
- if (mSeekBarView != null) {
+ if (mSeekBarView != null && mSeekBarView.getVisibility() != View.GONE) {
mSeekBarView.setVisibility(View.GONE);
+ mMetricsLogger.write(newLog(MetricsEvent.TYPE_CLOSE));
+ clearTimer();
+ } else if (mSeekBarView == null && controllerUpdated) {
+ // Only log if the controller changed, otherwise we would log multiple times for
+ // the same notification when user pauses/resumes
+ mMetricsLogger.write(newLog(MetricsEvent.TYPE_CLOSE));
}
return;
} else {
// Otherwise, make sure the seekbar is visible
- if (mSeekBarView != null) {
+ if (mSeekBarView != null && mSeekBarView.getVisibility() == View.GONE) {
mSeekBarView.setVisibility(View.VISIBLE);
+ mMetricsLogger.write(newLog(MetricsEvent.TYPE_OPEN));
}
}
}
@@ -153,6 +172,7 @@
stub.setLayoutInflater(layoutInflater);
stub.setLayoutResource(R.layout.notification_material_media_seekbar);
mSeekBarView = stub.inflate();
+ mMetricsLogger.write(newLog(MetricsEvent.TYPE_OPEN));
mSeekBar = mSeekBarView.findViewById(R.id.notification_media_progress_bar);
mSeekBar.setOnSeekBarChangeListener(mSeekListener);
@@ -161,17 +181,14 @@
mSeekBarTotalTime = mSeekBarView.findViewById(R.id.notification_media_total_time);
if (mSeekBarTimer == null) {
- // Disable seeking if it is not supported for this media session
- if (!canSeekMedia()) {
- mSeekBar.getThumb().setAlpha(0);
- mSeekBar.setEnabled(false);
+ if (canSeekMedia()) {
+ // Log initial state, since it will not be updated
+ mMetricsLogger.write(newLog(MetricsEvent.TYPE_DETAIL, 1));
} else {
- mSeekBar.getThumb().setAlpha(255);
- mSeekBar.setEnabled(true);
+ setScrubberVisible(false);
}
startTimer();
-
mMediaController.registerCallback(mMediaCallback);
}
}
@@ -209,6 +226,16 @@
return ((actions & PlaybackState.ACTION_SEEK_TO) != 0);
}
+ private void setScrubberVisible(boolean isVisible) {
+ if (mSeekBar == null || mSeekBar.isEnabled() == isVisible) {
+ return;
+ }
+
+ mSeekBar.getThumb().setAlpha(isVisible ? 255 : 0);
+ mSeekBar.setEnabled(isVisible);
+ mMetricsLogger.write(newLog(MetricsEvent.TYPE_DETAIL, isVisible ? 1 : 0));
+ }
+
protected final Runnable mUpdatePlaybackUi = new Runnable() {
@Override
public void run() {
@@ -228,6 +255,9 @@
mSeekBar.setProgress((int) position);
mSeekBarElapsedTime.setText(millisecondsToTimeString(position));
+
+ // Update scrubber in case available actions have changed
+ setScrubberVisible(canSeekMedia());
} else {
Log.d(TAG, "Controller missing data " + metadata + " " + playbackState);
clearTimer();
@@ -293,4 +323,28 @@
public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
return true;
}
+
+ /**
+ * Returns an initialized LogMaker for logging changes to the seekbar
+ * @return new LogMaker
+ */
+ private LogMaker newLog(int event) {
+ String packageName = mRow.getEntry().notification.getPackageName();
+
+ return new LogMaker(MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR)
+ .setType(event)
+ .setPackageName(packageName);
+ }
+
+ /**
+ * Returns an initialized LogMaker for logging changes with subtypes
+ * @return new LogMaker
+ */
+ private LogMaker newLog(int event, int subtype) {
+ String packageName = mRow.getEntry().notification.getPackageName();
+ return new LogMaker(MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR)
+ .setType(event)
+ .setSubtype(subtype)
+ .setPackageName(packageName);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index e5fbf63..78dc9a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -212,7 +212,9 @@
}
public boolean isDimmed() {
- return mDimmed;
+ // While we are expanding from pulse, we want the notifications not to be dimmed, otherwise
+ // you'd see the difference to the pulsing notification
+ return mDimmed && !(isPulseExpanding() && mDozeAmount == 1.0f);
}
public boolean isDark() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index dccf404..a517e76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -5709,6 +5709,7 @@
view.setTranslationY(wakeUplocation);
}
}
+ mDimmedNeedsAnimation = true;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
index 215aa77..a79b625 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
@@ -35,6 +36,8 @@
private final Context mContext;
private final WindowManager mWindowManager;
private final KeyButtonView mKeyButtonView;
+ private final int mDiameter;
+ private final int mMargin;
private KeyButtonDrawable mKeyButtonDrawable;
private boolean mIsShowing;
private boolean mCanShow = true;
@@ -46,6 +49,11 @@
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mKeyButtonView = (KeyButtonView) LayoutInflater.from(mContext).inflate(
R.layout.rotate_suggestion, null);
+ mKeyButtonView.setVisibility(View.VISIBLE);
+
+ Resources resources = mContext.getResources();
+ mDiameter = resources.getDimensionPixelSize(R.dimen.floating_rotation_button_diameter);
+ mMargin = resources.getDimensionPixelSize(R.dimen.floating_rotation_button_margin);
}
@Override
@@ -66,11 +74,8 @@
mIsShowing = true;
int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
- float density = mContext.getResources().getDisplayMetrics().density;
- int diameter = (int) density * 48;
- int margin = (int) density * 4;
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(diameter, diameter,
- margin, margin, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(mDiameter, mDiameter,
+ mMargin, mMargin, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("FloatingRotationButton");
@@ -131,7 +136,10 @@
@Override
public void setOnClickListener(View.OnClickListener onClickListener) {
- mKeyButtonView.setOnClickListener(onClickListener);
+ mKeyButtonView.setOnClickListener(view -> {
+ hide();
+ onClickListener.onClick(view);
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 337c6b1..d94a335 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -86,6 +86,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.ScreenDecorations;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.fragments.FragmentHostManager;
@@ -170,6 +171,7 @@
public int mDisplayId;
private boolean mIsOnDefaultDisplay;
public boolean mHomeBlockedThisTouch;
+ private ScreenDecorations mScreenDecorations;
private Handler mHandler = Dependency.get(Dependency.MAIN_HANDLER);
@@ -348,12 +350,17 @@
mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
}
setDisabled2Flags(mDisabledFlags2);
+
+ mScreenDecorations = SysUiServiceProvider.getComponent(getContext(),
+ ScreenDecorations.class);
+ getBarTransitions().addDarkIntensityListener(mScreenDecorations);
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (mNavigationBarView != null) {
+ mNavigationBarView.getBarTransitions().removeDarkIntensityListener(mScreenDecorations);
mNavigationBarView.getBarTransitions().destroy();
mNavigationBarView.getLightTransitionsController().destroy(getContext());
}
@@ -1020,7 +1027,7 @@
getBarTransitions().transitionTo(barMode, animate);
}
- private BarTransitions getBarTransitions() {
+ public NavigationBarTransitions getBarTransitions() {
return mNavigationBarView.getBarTransitions();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index 8a28c6f..2b5a28e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -36,9 +36,23 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import java.util.ArrayList;
+import java.util.List;
+
public final class NavigationBarTransitions extends BarTransitions implements
LightBarTransitionsController.DarkIntensityApplier {
+ /**
+ * Notified when the color of nav bar elements changes.
+ */
+ public interface DarkIntensityListener {
+ /**
+ * Called when the color of nav bar elements changes.
+ * @param darkIntensity 0 is the lightest color, 1 is the darkest.
+ */
+ void onDarkIntensity(float darkIntensity);
+ }
+
private final NavigationBarView mView;
private final IStatusBarService mBarService;
private final LightBarTransitionsController mLightTransitionsController;
@@ -49,6 +63,7 @@
private boolean mAutoDim;
private View mNavButtons;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
+ private List<DarkIntensityListener> mDarkIntensityListeners;
private final Handler mHandler = Handler.getMain();
private final IWallpaperVisibilityListener mWallpaperVisibilityListener =
@@ -69,6 +84,7 @@
mLightTransitionsController = new LightBarTransitionsController(view.getContext(), this);
mAllowAutoDimWallpaperNotVisible = view.getContext().getResources()
.getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper);
+ mDarkIntensityListeners = new ArrayList();
IWindowManager windowManagerService = Dependency.get(IWindowManager.class);
try {
@@ -168,12 +184,16 @@
applyDarkIntensity(mLightTransitionsController.getCurrentDarkIntensity());
}
+ @Override
public void applyDarkIntensity(float darkIntensity) {
SparseArray<ButtonDispatcher> buttonDispatchers = mView.getButtonDispatchers();
for (int i = buttonDispatchers.size() - 1; i >= 0; i--) {
buttonDispatchers.valueAt(i).setDarkIntensity(darkIntensity);
}
mView.getRotationButtonController().setDarkIntensity(darkIntensity);
+ for (DarkIntensityListener listener : mDarkIntensityListeners) {
+ listener.onDarkIntensity(darkIntensity);
+ }
if (mAutoDim) {
applyLightsOut(false, true);
}
@@ -190,4 +210,18 @@
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
}
+
+ /**
+ * Register {@code listener} to be notified when the color of nav bar elements changes.
+ */
+ public void addDarkIntensityListener(DarkIntensityListener listener) {
+ mDarkIntensityListeners.add(listener);
+ }
+
+ /**
+ * Remove {@code listener} from being notified when the color of nav bar elements changes.
+ */
+ public void removeDarkIntensityListener(DarkIntensityListener listener) {
+ mDarkIntensityListeners.remove(listener);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 7682e8a..8a895e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -299,7 +299,7 @@
return mTintController;
}
- public BarTransitions getBarTransitions() {
+ public NavigationBarTransitions getBarTransitions() {
return mBarTransitions;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
index f1460a6..b117dec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
@@ -66,7 +66,9 @@
@Override
public void onDestroy() {
- mRotationButtonController.cleanUp();
+ if (mRotationButtonController != null) {
+ mRotationButtonController.cleanUp();
+ }
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 43ea92f..13b9d56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -15,6 +15,7 @@
package com.android.systemui.statusbar.notification.row;
import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
+import static android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -100,8 +101,31 @@
@Test
public void testNoAppOpsInSlowSwipe() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 0);
+ Settings.Secure.putInt(mContext.getContentResolver(), SHOW_NOTIFICATION_SNOOZE, 0);
+
+ NotificationMenuRow row = new NotificationMenuRow(mContext);
+ row.createMenu(mRow, null);
+
+ ViewGroup container = (ViewGroup) row.getMenuView();
+ // noti blocking
+ assertEquals(1, container.getChildCount());
+ }
+
+ @Test
+ public void testNoSnoozeInSlowSwipe() {
+ Settings.Secure.putInt(mContext.getContentResolver(), SHOW_NOTIFICATION_SNOOZE, 0);
+
+ NotificationMenuRow row = new NotificationMenuRow(mContext);
+ row.createMenu(mRow, null);
+
+ ViewGroup container = (ViewGroup) row.getMenuView();
+ // just for noti blocking
+ assertEquals(1, container.getChildCount());
+ }
+
+ @Test
+ public void testSnoozeInSlowSwipe() {
+ Settings.Secure.putInt(mContext.getContentResolver(), SHOW_NOTIFICATION_SNOOZE, 1);
NotificationMenuRow row = new NotificationMenuRow(mContext);
row.createMenu(mRow, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
new file mode 100644
index 0000000..df41a84
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Notification;
+import android.media.MediaMetadata;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.widget.RemoteViews;
+import android.widget.SeekBar;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class NotificationMediaTemplateViewWrapperTest extends SysuiTestCase {
+
+ private ExpandableNotificationRow mRow;
+ private Notification mNotif;
+ private View mView;
+ private NotificationMediaTemplateViewWrapper mWrapper;
+
+ @Mock
+ private MetricsLogger mMetricsLogger;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+
+ mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
+ }
+
+ private void makeTestNotification(long duration, boolean allowSeeking) throws Exception {
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text");
+
+ MediaMetadata metadata = new MediaMetadata.Builder()
+ .putLong(MediaMetadata.METADATA_KEY_DURATION, duration)
+ .build();
+ MediaSession session = new MediaSession(mContext, "TEST_CHANNEL");
+ session.setMetadata(metadata);
+
+ PlaybackState playbackState = new PlaybackState.Builder()
+ .setActions(allowSeeking ? PlaybackState.ACTION_SEEK_TO : 0)
+ .build();
+
+ session.setPlaybackState(playbackState);
+
+ builder.setStyle(new Notification.MediaStyle()
+ .setMediaSession(session.getSessionToken())
+ );
+
+ mNotif = builder.build();
+ assertTrue(mNotif.hasMediaSession());
+
+ mRow = new NotificationTestHelper(mContext).createRow(mNotif);
+
+ RemoteViews views = new RemoteViews(mContext.getPackageName(),
+ com.android.internal.R.layout.notification_template_material_big_media);
+ mView = views.apply(mContext, null);
+ mWrapper = new NotificationMediaTemplateViewWrapper(mContext,
+ mView, mRow);
+ mWrapper.onContentUpdated(mRow);
+ }
+
+ @Test
+ public void testLogging_NoSeekbar() throws Exception {
+ // Media sessions with duration <= 0 should not include a seekbar
+ makeTestNotification(0, false);
+
+ verify(mMetricsLogger).write(argThat(logMaker ->
+ logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
+ && logMaker.getType() == MetricsEvent.TYPE_CLOSE
+ ));
+
+ verify(mMetricsLogger, times(0)).write(argThat(logMaker ->
+ logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
+ && logMaker.getType() == MetricsEvent.TYPE_OPEN
+ ));
+ }
+
+ @Test
+ public void testLogging_HasSeekbarNoScrubber() throws Exception {
+ // Media sessions that do not support seeking should have a seekbar, but no scrubber
+ makeTestNotification(1000, false);
+
+ verify(mMetricsLogger).write(argThat(logMaker ->
+ logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
+ && logMaker.getType() == MetricsEvent.TYPE_OPEN
+ ));
+
+ // Ensure the callback runs at least once
+ mWrapper.mUpdatePlaybackUi.run();
+
+ verify(mMetricsLogger).write(argThat(logMaker ->
+ logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
+ && logMaker.getType() == MetricsEvent.TYPE_DETAIL
+ && logMaker.getSubtype() == 0
+ ));
+ }
+
+ @Test
+ public void testLogging_HasSeekbarAndScrubber() throws Exception {
+ makeTestNotification(1000, true);
+
+ verify(mMetricsLogger).write(argThat(logMaker ->
+ logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
+ && logMaker.getType() == MetricsEvent.TYPE_OPEN
+ ));
+
+ verify(mMetricsLogger).write(argThat(logMaker ->
+ logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
+ && logMaker.getType() == MetricsEvent.TYPE_DETAIL
+ && logMaker.getSubtype() == 1
+ ));
+ }
+
+ @Test
+ public void testLogging_UpdateSeekbar() throws Exception {
+ makeTestNotification(1000, true);
+
+ SeekBar seekbar = mView.findViewById(
+ com.android.internal.R.id.notification_media_progress_bar);
+ assertTrue(seekbar != null);
+
+ mWrapper.mSeekListener.onStopTrackingTouch(seekbar);
+
+ verify(mMetricsLogger).write(argThat(logMaker ->
+ logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
+ && logMaker.getType() == MetricsEvent.TYPE_UPDATE));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 4181d38..faf5a97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -268,7 +268,7 @@
when(view.getRecentsButton()).thenReturn(mock(ButtonDispatcher.class));
when(view.getAccessibilityButton()).thenReturn(mock(ButtonDispatcher.class));
when(view.getRotateSuggestionButton()).thenReturn(mock(RotationContextButton.class));
- when(view.getBarTransitions()).thenReturn(mock(BarTransitions.class));
+ when(view.getBarTransitions()).thenReturn(mock(NavigationBarTransitions.class));
when(view.getLightTransitionsController()).thenReturn(
mock(LightBarTransitionsController.class));
when(view.getRotationButtonController()).thenReturn(
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 3061981..0f0e6f9 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7379,6 +7379,15 @@
// Custom tag for NotificationItem. Hash of the NAS that made adjustments.
FIELD_NOTIFICATION_ASSISTANT_SERVICE_HASH = 1742;
+ // Report interactions with seekbar on media notifications
+ // OPEN: Seekbar is visible
+ // CLOSE: Seekbar is not visible
+ // DETAIL: Seekbar scrubber enabled / disabled
+ // Subtype: 0 disabled, cannot seek; 1 enabled, can seek
+ // UPDATE: Scrubber was moved by user
+ // CATEGORY: NOTIFICATION
+ MEDIA_NOTIFICATION_SEEKBAR = 1743;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index dfbb55a..d0edaaa 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1763,7 +1763,7 @@
+ ", callingPackage: " + callingPackage;
// STOPSHIP (b/128866264): Just to catch breakages. Remove before final release.
Slog.wtf(TAG, errorMsg);
- throw new UnsupportedOperationException(errorMsg);
+ throw new IllegalStateException(errorMsg);
}
setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
interval, operation, directReceiver, listenerTag, flags, true, workSource,
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0a1dbff..45f7360 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -4363,7 +4363,7 @@
/**
* @return VPN information for accounting, or null if we can't retrieve all required
- * information, e.g primary underlying iface.
+ * information, e.g underlying ifaces.
*/
@Nullable
private VpnInfo createVpnInfo(Vpn vpn) {
@@ -4375,17 +4375,24 @@
// see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
// the underlyingNetworks list.
if (underlyingNetworks == null) {
- NetworkAgentInfo defaultNetwork = getDefaultNetwork();
- if (defaultNetwork != null && defaultNetwork.linkProperties != null) {
- info.primaryUnderlyingIface = getDefaultNetwork().linkProperties.getInterfaceName();
- }
- } else if (underlyingNetworks.length > 0) {
- LinkProperties linkProperties = getLinkProperties(underlyingNetworks[0]);
- if (linkProperties != null) {
- info.primaryUnderlyingIface = linkProperties.getInterfaceName();
+ NetworkAgentInfo defaultNai = getDefaultNetwork();
+ if (defaultNai != null) {
+ underlyingNetworks = new Network[] { defaultNai.network };
}
}
- return info.primaryUnderlyingIface == null ? null : info;
+ if (underlyingNetworks != null && underlyingNetworks.length > 0) {
+ List<String> interfaces = new ArrayList<>();
+ for (Network network : underlyingNetworks) {
+ LinkProperties lp = getLinkProperties(network);
+ if (lp != null && !TextUtils.isEmpty(lp.getInterfaceName())) {
+ interfaces.add(lp.getInterfaceName());
+ }
+ }
+ if (!interfaces.isEmpty()) {
+ info.underlyingIfaces = interfaces.toArray(new String[interfaces.size()]);
+ }
+ }
+ return info.underlyingIfaces == null ? null : info;
}
/**
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index bbbec66..d2b992b 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2842,8 +2842,9 @@
}
}
+ /** Not thread safe */
class AppFuseMountScope extends AppFuseBridge.MountScope {
- boolean opened = false;
+ private boolean mMounted = false;
public AppFuseMountScope(int uid, int mountId) {
super(uid, mountId);
@@ -2852,8 +2853,9 @@
@Override
public ParcelFileDescriptor open() throws NativeDaemonConnectorException {
try {
- return new ParcelFileDescriptor(
- mVold.mountAppFuse(uid, mountId));
+ final FileDescriptor fd = mVold.mountAppFuse(uid, mountId);
+ mMounted = true;
+ return new ParcelFileDescriptor(fd);
} catch (Exception e) {
throw new NativeDaemonConnectorException("Failed to mount", e);
}
@@ -2872,9 +2874,9 @@
@Override
public void close() throws Exception {
- if (opened) {
+ if (mMounted) {
mVold.unmountAppFuse(uid, mountId);
- opened = false;
+ mMounted = false;
}
}
}
@@ -3582,6 +3584,10 @@
}
final String[] packagesForUid = mIPackageManager.getPackagesForUid(uid);
+ if (ArrayUtils.isEmpty(packagesForUid)) {
+ // It's possible the package got uninstalled already, so just ignore.
+ return Zygote.MOUNT_EXTERNAL_NONE;
+ }
if (packageName == null) {
packageName = packagesForUid[0];
}
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index bdbff3d..4b48ef9 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -661,12 +661,6 @@
return mTestUserKeyFile == null ? getAdbFile(ADB_KEYS_FILE) : mTestUserKeyFile;
}
- private void createKeyFile(File keyFile) throws IOException {
- keyFile.createNewFile();
- FileUtils.setPermissions(keyFile.toString(),
- FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP, -1, -1);
- }
-
private void writeKey(String key) {
try {
File keyFile = getUserKeyFile();
@@ -675,14 +669,13 @@
return;
}
- if (!keyFile.exists()) {
- createKeyFile(keyFile);
- }
-
FileOutputStream fo = new FileOutputStream(keyFile, true);
fo.write(key.getBytes());
fo.write('\n');
fo.close();
+
+ FileUtils.setPermissions(keyFile.toString(),
+ FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP, -1, -1);
} catch (IOException ex) {
Slog.e(TAG, "Error writing key:" + ex);
}
@@ -698,10 +691,6 @@
return;
}
- if (!keyFile.exists()) {
- createKeyFile(keyFile);
- }
-
atomicKeyFile = new AtomicFile(keyFile);
fo = atomicKeyFile.startWrite();
for (String key : keys) {
@@ -709,6 +698,9 @@
fo.write('\n');
}
atomicKeyFile.finishWrite(fo);
+
+ FileUtils.setPermissions(keyFile.toString(),
+ FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP, -1, -1);
} catch (IOException ex) {
Slog.e(TAG, "Error writing keys: " + ex);
if (atomicKeyFile != null) {
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index af25ad5..b279b37 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -775,8 +775,7 @@
// if the application has the permission, let it to access user's clipboard.
// To passed synthesized uid user#10_app#systemui may not tell the real uid.
// userId must pass intending userId. i.e. user#10.
- allowed = mContentCaptureInternal.isContentCaptureServiceForUser(
- Binder.getCallingUid(), userId);
+ allowed = mContentCaptureInternal.isContentCaptureServiceForUser(uid, userId);
}
if (!allowed && mAutofillInternal != null) {
// ...or the Augmented Autofill Service
@@ -785,8 +784,7 @@
// if the application has the permission, let it to access user's clipboard.
// To passed synthesized uid user#10_app#systemui may not tell the real uid.
// userId must pass intending userId. i.e. user#10.
- allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(
- Binder.getCallingUid(), userId);
+ allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId);
}
if (!allowed) {
Slog.e(TAG, "Denying clipboard access to " + callingPackage
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
index f560d69..ef6944e 100644
--- a/services/core/java/com/android/server/job/controllers/QuotaController.java
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -16,6 +16,10 @@
package com.android.server.job.controllers;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+
import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
@@ -75,17 +79,28 @@
/**
* Controller that tracks whether an app has exceeded its standby bucket quota.
*
- * Each job in each bucket is given 10 minutes to run within its respective time window. Active
- * jobs can run indefinitely, working set jobs can run for 10 minutes within a 2 hour window,
- * frequent jobs get to run 10 minutes in an 8 hour window, and rare jobs get to run 10 minutes in
- * a 24 hour window. The windows are rolling, so as soon as a job would have some quota based on its
- * bucket, it will be eligible to run. When a job's bucket changes, its new quota is immediately
- * applied to it.
+ * With initial defaults, each app in each bucket is given 10 minutes to run within its respective
+ * time window. Active jobs can run indefinitely, working set jobs can run for 10 minutes within a
+ * 2 hour window, frequent jobs get to run 10 minutes in an 8 hour window, and rare jobs get to run
+ * 10 minutes in a 24 hour window. The windows are rolling, so as soon as a job would have some
+ * quota based on its bucket, it will be eligible to run. When a job's bucket changes, its new
+ * quota is immediately applied to it.
+ *
+ * Job and session count limits are included to prevent abuse/spam. Each bucket has its own limit on
+ * the number of jobs or sessions that can run within the window. Regardless of bucket, apps will
+ * not be allowed to run more than 20 jobs within the past 10 minutes.
*
* Jobs are throttled while an app is not in a foreground state. All jobs are allowed to run
* freely when an app enters the foreground state and are restricted when the app leaves the
- * foreground state. However, jobs that are started while the app is in the TOP state are not
- * restricted regardless of the app's state change.
+ * foreground state. However, jobs that are started while the app is in the TOP state do not count
+ * towards any quota and are not restricted regardless of the app's state change.
+ *
+ * Jobs will not be throttled when the device is charging. The device is considered to be charging
+ * once the {@link BatteryManager#ACTION_CHARGING} intent has been broadcast.
+ *
+ * Note: all limits are enforced per bucket window unless explicitly stated otherwise.
+ * All stated values are configurable and subject to change. See {@link QcConstants} for current
+ * defaults.
*
* Test: atest com.android.server.job.controllers.QuotaControllerTest
*/
@@ -94,8 +109,6 @@
private static final boolean DEBUG = JobSchedulerService.DEBUG
|| Log.isLoggable(TAG, Log.DEBUG);
- private static final long MINUTE_IN_MILLIS = 60 * 1000L;
-
private static final String ALARM_TAG_CLEANUP = "*job.cleanup*";
private static final String ALARM_TAG_QUOTA_CHECK = "*job.quota_check*";
@@ -244,6 +257,8 @@
public long expirationTimeElapsed;
public long windowSizeMs;
+ public int jobCountLimit;
+ public int sessionCountLimit;
/** The total amount of time the app ran in its respective bucket window size. */
public long executionTimeInWindowMs;
@@ -260,54 +275,58 @@
public int sessionCountInWindow;
/**
- * The time after which the sum of all the app's sessions plus {@link #mQuotaBufferMs}
- * equals the quota. This is only valid if
- * executionTimeInWindowMs >= {@link #mAllowedTimePerPeriodMs} or
- * executionTimeInMaxPeriodMs >= {@link #mMaxExecutionTimeMs}.
+ * The time after which the app will be under the bucket quota and can start running jobs
+ * again. This is only valid if
+ * {@link #executionTimeInWindowMs} >= {@link #mAllowedTimePerPeriodMs},
+ * {@link #executionTimeInMaxPeriodMs} >= {@link #mMaxExecutionTimeMs},
+ * {@link #bgJobCountInWindow} >= {@link #jobCountLimit}, or
+ * {@link #sessionCountInWindow} >= {@link #sessionCountLimit}.
*/
- public long quotaCutoffTimeElapsed;
+ public long inQuotaTimeElapsed;
/**
- * The time after which {@link #jobCountInAllowedTime} should be considered invalid, in the
- * elapsed realtime timebase.
+ * The time after which {@link #jobCountInRateLimitingWindow} should be considered invalid,
+ * in the elapsed realtime timebase.
*/
- public long jobCountExpirationTimeElapsed;
+ public long jobRateLimitExpirationTimeElapsed;
/**
- * The number of jobs that ran in at least the last {@link #mAllowedTimePerPeriodMs}.
+ * The number of jobs that ran in at least the last {@link #mRateLimitingWindowMs}.
* It may contain a few stale entries since cleanup won't happen exactly every
- * {@link #mAllowedTimePerPeriodMs}.
+ * {@link #mRateLimitingWindowMs}.
*/
- public int jobCountInAllowedTime;
+ public int jobCountInRateLimitingWindow;
/**
- * The time after which {@link #sessionCountInAllowedTime} should be considered
+ * The time after which {@link #sessionCountInRateLimitingWindow} should be considered
* invalid, in the elapsed realtime timebase.
*/
- public long sessionCountExpirationTimeElapsed;
+ public long sessionRateLimitExpirationTimeElapsed;
/**
* The number of {@link TimingSession}s that ran in at least the last
- * {@link #mAllowedTimePerPeriodMs}. It may contain a few stale entries since cleanup won't
- * happen exactly every {@link #mAllowedTimePerPeriodMs}. This should only be considered
- * valid before elapsed realtime has reached {@link #sessionCountExpirationTimeElapsed}.
+ * {@link #mRateLimitingWindowMs}. It may contain a few stale entries since cleanup won't
+ * happen exactly every {@link #mRateLimitingWindowMs}. This should only be considered
+ * valid before elapsed realtime has reached {@link #sessionRateLimitExpirationTimeElapsed}.
*/
- public int sessionCountInAllowedTime;
+ public int sessionCountInRateLimitingWindow;
@Override
public String toString() {
return "expirationTime=" + expirationTimeElapsed + ", "
- + "windowSize=" + windowSizeMs + ", "
+ + "windowSizeMs=" + windowSizeMs + ", "
+ + "jobCountLimit=" + jobCountLimit + ", "
+ + "sessionCountLimit=" + sessionCountLimit + ", "
+ "executionTimeInWindow=" + executionTimeInWindowMs + ", "
+ "bgJobCountInWindow=" + bgJobCountInWindow + ", "
+ "executionTimeInMaxPeriod=" + executionTimeInMaxPeriodMs + ", "
+ "bgJobCountInMaxPeriod=" + bgJobCountInMaxPeriod + ", "
+ "sessionCountInWindow=" + sessionCountInWindow + ", "
- + "quotaCutoffTime=" + quotaCutoffTimeElapsed + ", "
- + "jobCountExpirationTime=" + jobCountExpirationTimeElapsed + ", "
- + "jobCountInAllowedTime=" + jobCountInAllowedTime + ", "
- + "sessionCountExpirationTime=" + sessionCountExpirationTimeElapsed + ", "
- + "sessionCountInAllowedTime=" + sessionCountInAllowedTime;
+ + "inQuotaTime=" + inQuotaTimeElapsed + ", "
+ + "jobCountExpirationTime=" + jobRateLimitExpirationTimeElapsed + ", "
+ + "jobCountInRateLimitingWindow=" + jobCountInRateLimitingWindow + ", "
+ + "sessionCountExpirationTime=" + sessionRateLimitExpirationTimeElapsed + ", "
+ + "sessionCountInRateLimitingWindow=" + sessionCountInRateLimitingWindow;
}
@Override
@@ -316,18 +335,21 @@
ExecutionStats other = (ExecutionStats) obj;
return this.expirationTimeElapsed == other.expirationTimeElapsed
&& this.windowSizeMs == other.windowSizeMs
+ && this.jobCountLimit == other.jobCountLimit
+ && this.sessionCountLimit == other.sessionCountLimit
&& this.executionTimeInWindowMs == other.executionTimeInWindowMs
&& this.bgJobCountInWindow == other.bgJobCountInWindow
&& this.executionTimeInMaxPeriodMs == other.executionTimeInMaxPeriodMs
&& this.sessionCountInWindow == other.sessionCountInWindow
&& this.bgJobCountInMaxPeriod == other.bgJobCountInMaxPeriod
- && this.quotaCutoffTimeElapsed == other.quotaCutoffTimeElapsed
- && this.jobCountExpirationTimeElapsed == other.jobCountExpirationTimeElapsed
- && this.jobCountInAllowedTime == other.jobCountInAllowedTime
- && this.sessionCountExpirationTimeElapsed
- == other.sessionCountExpirationTimeElapsed
- && this.sessionCountInAllowedTime
- == other.sessionCountInAllowedTime;
+ && this.inQuotaTimeElapsed == other.inQuotaTimeElapsed
+ && this.jobRateLimitExpirationTimeElapsed
+ == other.jobRateLimitExpirationTimeElapsed
+ && this.jobCountInRateLimitingWindow == other.jobCountInRateLimitingWindow
+ && this.sessionRateLimitExpirationTimeElapsed
+ == other.sessionRateLimitExpirationTimeElapsed
+ && this.sessionCountInRateLimitingWindow
+ == other.sessionCountInRateLimitingWindow;
} else {
return false;
}
@@ -338,16 +360,18 @@
int result = 0;
result = 31 * result + hashLong(expirationTimeElapsed);
result = 31 * result + hashLong(windowSizeMs);
+ result = 31 * result + hashLong(jobCountLimit);
+ result = 31 * result + hashLong(sessionCountLimit);
result = 31 * result + hashLong(executionTimeInWindowMs);
result = 31 * result + bgJobCountInWindow;
result = 31 * result + hashLong(executionTimeInMaxPeriodMs);
result = 31 * result + bgJobCountInMaxPeriod;
result = 31 * result + sessionCountInWindow;
- result = 31 * result + hashLong(quotaCutoffTimeElapsed);
- result = 31 * result + hashLong(jobCountExpirationTimeElapsed);
- result = 31 * result + jobCountInAllowedTime;
- result = 31 * result + hashLong(sessionCountExpirationTimeElapsed);
- result = 31 * result + sessionCountInAllowedTime;
+ result = 31 * result + hashLong(inQuotaTimeElapsed);
+ result = 31 * result + hashLong(jobRateLimitExpirationTimeElapsed);
+ result = 31 * result + jobCountInRateLimitingWindow;
+ result = 31 * result + hashLong(sessionRateLimitExpirationTimeElapsed);
+ result = 31 * result + sessionCountInRateLimitingWindow;
return result;
}
}
@@ -399,19 +423,19 @@
private boolean mShouldThrottle;
/** How much time each app will have to run jobs within their standby bucket window. */
- private long mAllowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
+ private long mAllowedTimePerPeriodMs = QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
/**
* The maximum amount of time an app can have its jobs running within a {@link #MAX_PERIOD_MS}
* window.
*/
- private long mMaxExecutionTimeMs = 4 * 60 * MINUTE_IN_MILLIS;
+ private long mMaxExecutionTimeMs = QcConstants.DEFAULT_MAX_EXECUTION_TIME_MS;
/**
* How much time the app should have before transitioning from out-of-quota to in-quota.
* This should not affect processing if the app is already in-quota.
*/
- private long mQuotaBufferMs = 30 * 1000L; // 30 seconds
+ private long mQuotaBufferMs = QcConstants.DEFAULT_IN_QUOTA_BUFFER_MS;
/**
* {@link #mAllowedTimePerPeriodMs} - {@link #mQuotaBufferMs}. This can be used to determine
@@ -425,14 +449,19 @@
*/
private long mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
- /** The maximum number of jobs that can run within the past {@link #mAllowedTimePerPeriodMs}. */
- private int mMaxJobCountPerAllowedTime = 20;
+ /** The period of time used to rate limit recently run jobs. */
+ private long mRateLimitingWindowMs = QcConstants.DEFAULT_RATE_LIMITING_WINDOW_MS;
+
+ /** The maximum number of jobs that can run within the past {@link #mRateLimitingWindowMs}. */
+ private int mMaxJobCountPerRateLimitingWindow =
+ QcConstants.DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
/**
* The maximum number of {@link TimingSession}s that can run within the past {@link
- * #mAllowedTimePerPeriodMs}.
+ * #mRateLimitingWindowMs}.
*/
- private int mMaxSessionCountPerAllowedTime = 20;
+ private int mMaxSessionCountPerRateLimitingWindow =
+ QcConstants.DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW;
private long mNextCleanupTimeElapsed = 0;
private final AlarmManager.OnAlarmListener mSessionCleanupAlarmListener =
@@ -486,11 +515,11 @@
* The rolling window size for each standby bucket. Within each window, an app will have 10
* minutes to run its jobs.
*/
- private final long[] mBucketPeriodsMs = new long[] {
- 10 * MINUTE_IN_MILLIS, // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time
- 2 * 60 * MINUTE_IN_MILLIS, // 2 hours for WORKING
- 8 * 60 * MINUTE_IN_MILLIS, // 8 hours for FREQUENT
- 24 * 60 * MINUTE_IN_MILLIS // 24 hours for RARE
+ private final long[] mBucketPeriodsMs = new long[]{
+ QcConstants.DEFAULT_WINDOW_SIZE_ACTIVE_MS,
+ QcConstants.DEFAULT_WINDOW_SIZE_WORKING_MS,
+ QcConstants.DEFAULT_WINDOW_SIZE_FREQUENT_MS,
+ QcConstants.DEFAULT_WINDOW_SIZE_RARE_MS
};
/** The maximum period any bucket can have. */
@@ -503,16 +532,13 @@
*
* @see #mBucketPeriodsMs
*/
- private final int[] mMaxBucketJobCounts = new int[] {
- 200, // ACTIVE -- 1200/hr
- 1200, // WORKING -- 600/hr
- 1800, // FREQUENT -- 225/hr
- 2400 // RARE -- 100/hr
+ private final int[] mMaxBucketJobCounts = new int[]{
+ QcConstants.DEFAULT_MAX_JOB_COUNT_ACTIVE,
+ QcConstants.DEFAULT_MAX_JOB_COUNT_WORKING,
+ QcConstants.DEFAULT_MAX_JOB_COUNT_FREQUENT,
+ QcConstants.DEFAULT_MAX_JOB_COUNT_RARE
};
- /** The minimum number of jobs that any bucket will be allowed to run. */
- private static final int MIN_BUCKET_JOB_COUNT = 100;
-
/**
* The maximum number of {@link TimingSession}s based on its standby bucket. For each max value
* count in the array, the app will not be allowed to have more than that many number of
@@ -527,14 +553,12 @@
QcConstants.DEFAULT_MAX_SESSION_COUNT_RARE
};
- /** The minimum number of {@link TimingSession}s that any bucket will be allowed to run. */
- private static final int MIN_BUCKET_SESSION_COUNT = 3;
-
/**
* Treat two distinct {@link TimingSession}s as the same if they start and end within this
* amount of time of each other.
*/
- private long mTimingSessionCoalescingDurationMs = 0;
+ private long mTimingSessionCoalescingDurationMs =
+ QcConstants.DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS;
/** An app has reached its quota. The message should contain a {@link Package} object. */
private static final int MSG_REACHED_QUOTA = 0;
@@ -761,8 +785,8 @@
final int standbyBucket) {
final long now = sElapsedRealtimeClock.millis();
final boolean isUnderAllowedTimeQuota =
- (stats.jobCountExpirationTimeElapsed <= now
- || stats.jobCountInAllowedTime < mMaxJobCountPerAllowedTime);
+ (stats.jobRateLimitExpirationTimeElapsed <= now
+ || stats.jobCountInRateLimitingWindow < mMaxJobCountPerRateLimitingWindow);
return isUnderAllowedTimeQuota
&& (stats.bgJobCountInWindow < mMaxBucketJobCounts[standbyBucket]);
}
@@ -770,10 +794,8 @@
private boolean isUnderSessionCountQuotaLocked(@NonNull ExecutionStats stats,
final int standbyBucket) {
final long now = sElapsedRealtimeClock.millis();
- final boolean isUnderAllowedTimeQuota =
- (stats.sessionCountExpirationTimeElapsed <= now
- || stats.sessionCountInAllowedTime
- < mMaxSessionCountPerAllowedTime);
+ final boolean isUnderAllowedTimeQuota = (stats.sessionRateLimitExpirationTimeElapsed <= now
+ || stats.sessionCountInRateLimitingWindow < mMaxSessionCountPerRateLimitingWindow);
return isUnderAllowedTimeQuota
&& stats.sessionCountInWindow < mMaxBucketSessionCounts[standbyBucket];
}
@@ -924,12 +946,18 @@
}
if (refreshStatsIfOld) {
final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
+ final int jobCountLimit = mMaxBucketJobCounts[standbyBucket];
+ final int sessionCountLimit = mMaxBucketSessionCounts[standbyBucket];
Timer timer = mPkgTimers.get(userId, packageName);
if ((timer != null && timer.isActive())
|| stats.expirationTimeElapsed <= sElapsedRealtimeClock.millis()
- || stats.windowSizeMs != bucketWindowSizeMs) {
+ || stats.windowSizeMs != bucketWindowSizeMs
+ || stats.jobCountLimit != jobCountLimit
+ || stats.sessionCountLimit != sessionCountLimit) {
// The stats are no longer valid.
stats.windowSizeMs = bucketWindowSizeMs;
+ stats.jobCountLimit = jobCountLimit;
+ stats.sessionCountLimit = sessionCountLimit;
updateExecutionStatsLocked(userId, packageName, stats);
}
}
@@ -945,7 +973,7 @@
stats.executionTimeInMaxPeriodMs = 0;
stats.bgJobCountInMaxPeriod = 0;
stats.sessionCountInWindow = 0;
- stats.quotaCutoffTimeElapsed = 0;
+ stats.inQuotaTimeElapsed = 0;
Timer timer = mPkgTimers.get(userId, packageName);
final long nowElapsed = sElapsedRealtimeClock.millis();
@@ -958,12 +986,12 @@
// invalidate now.
stats.expirationTimeElapsed = nowElapsed;
if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
- stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
- nowElapsed - mAllowedTimeIntoQuotaMs);
+ stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
+ nowElapsed - mAllowedTimeIntoQuotaMs + stats.windowSizeMs);
}
if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) {
- stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
- nowElapsed - mMaxExecutionTimeIntoQuotaMs);
+ stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
+ nowElapsed - mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS);
}
}
@@ -985,36 +1013,40 @@
TimingSession session = sessions.get(i);
// Window management.
- if (startWindowElapsed < session.startTimeElapsed) {
- stats.executionTimeInWindowMs += session.endTimeElapsed - session.startTimeElapsed;
+ if (startWindowElapsed < session.endTimeElapsed) {
+ final long start;
+ if (startWindowElapsed < session.startTimeElapsed) {
+ start = session.startTimeElapsed;
+ emptyTimeMs =
+ Math.min(emptyTimeMs, session.startTimeElapsed - startWindowElapsed);
+ } else {
+ // The session started before the window but ended within the window. Only
+ // include the portion that was within the window.
+ start = startWindowElapsed;
+ emptyTimeMs = 0;
+ }
+
+ stats.executionTimeInWindowMs += session.endTimeElapsed - start;
stats.bgJobCountInWindow += session.bgJobCount;
- emptyTimeMs = Math.min(emptyTimeMs, session.startTimeElapsed - startWindowElapsed);
if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
- stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
- session.startTimeElapsed + stats.executionTimeInWindowMs
- - mAllowedTimeIntoQuotaMs);
+ stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
+ start + stats.executionTimeInWindowMs - mAllowedTimeIntoQuotaMs
+ + stats.windowSizeMs);
+ }
+ if (stats.bgJobCountInWindow >= stats.jobCountLimit) {
+ stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
+ session.endTimeElapsed + stats.windowSizeMs);
}
if (i == loopStart
|| (sessions.get(i + 1).startTimeElapsed - session.endTimeElapsed)
> mTimingSessionCoalescingDurationMs) {
// Coalesce sessions if they are very close to each other in time
sessionCountInWindow++;
- }
- } else if (startWindowElapsed < session.endTimeElapsed) {
- // The session started before the window but ended within the window. Only include
- // the portion that was within the window.
- stats.executionTimeInWindowMs += session.endTimeElapsed - startWindowElapsed;
- stats.bgJobCountInWindow += session.bgJobCount;
- emptyTimeMs = 0;
- if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
- stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
- startWindowElapsed + stats.executionTimeInWindowMs
- - mAllowedTimeIntoQuotaMs);
- }
- if (i == loopStart
- || (sessions.get(i + 1).startTimeElapsed - session.endTimeElapsed)
- > mTimingSessionCoalescingDurationMs) {
- sessionCountInWindow++;
+
+ if (sessionCountInWindow >= stats.sessionCountLimit) {
+ stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
+ session.endTimeElapsed + stats.windowSizeMs);
+ }
}
}
@@ -1025,9 +1057,9 @@
stats.bgJobCountInMaxPeriod += session.bgJobCount;
emptyTimeMs = Math.min(emptyTimeMs, session.startTimeElapsed - startMaxElapsed);
if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) {
- stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
+ stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
session.startTimeElapsed + stats.executionTimeInMaxPeriodMs
- - mMaxExecutionTimeIntoQuotaMs);
+ - mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS);
}
} else if (startMaxElapsed < session.endTimeElapsed) {
// The session started before the window but ended within the window. Only include
@@ -1036,9 +1068,9 @@
stats.bgJobCountInMaxPeriod += session.bgJobCount;
emptyTimeMs = 0;
if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) {
- stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
+ stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
startMaxElapsed + stats.executionTimeInMaxPeriodMs
- - mMaxExecutionTimeIntoQuotaMs);
+ - mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS);
}
} else {
// This session ended before the window. No point in going any further.
@@ -1094,11 +1126,11 @@
stats = new ExecutionStats();
appStats[i] = stats;
}
- if (stats.jobCountExpirationTimeElapsed <= now) {
- stats.jobCountExpirationTimeElapsed = now + mAllowedTimePerPeriodMs;
- stats.jobCountInAllowedTime = 0;
+ if (stats.jobRateLimitExpirationTimeElapsed <= now) {
+ stats.jobRateLimitExpirationTimeElapsed = now + mRateLimitingWindowMs;
+ stats.jobCountInRateLimitingWindow = 0;
}
- stats.jobCountInAllowedTime += count;
+ stats.jobCountInRateLimitingWindow += count;
}
}
@@ -1115,11 +1147,11 @@
stats = new ExecutionStats();
appStats[i] = stats;
}
- if (stats.sessionCountExpirationTimeElapsed <= now) {
- stats.sessionCountExpirationTimeElapsed = now + mAllowedTimePerPeriodMs;
- stats.sessionCountInAllowedTime = 0;
+ if (stats.sessionRateLimitExpirationTimeElapsed <= now) {
+ stats.sessionRateLimitExpirationTimeElapsed = now + mRateLimitingWindowMs;
+ stats.sessionCountInRateLimitingWindow = 0;
}
- stats.sessionCountInAllowedTime++;
+ stats.sessionCountInRateLimitingWindow++;
}
}
@@ -1367,18 +1399,17 @@
}
// The time this app will have quota again.
- long inQuotaTimeElapsed = stats.quotaCutoffTimeElapsed + stats.windowSizeMs;
- if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeMs) {
+ long inQuotaTimeElapsed = stats.inQuotaTimeElapsed;
+ if (!isUnderJobCountQuota && stats.bgJobCountInWindow < stats.jobCountLimit) {
+ // App hit the rate limit.
inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
- stats.quotaCutoffTimeElapsed + MAX_PERIOD_MS);
+ stats.jobRateLimitExpirationTimeElapsed);
}
- if (!isUnderJobCountQuota) {
+ if (!isUnderTimingSessionCountQuota
+ && stats.sessionCountInWindow < stats.sessionCountLimit) {
+ // App hit the rate limit.
inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
- stats.jobCountExpirationTimeElapsed + mAllowedTimePerPeriodMs);
- }
- if (!isUnderTimingSessionCountQuota) {
- inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
- stats.sessionCountExpirationTimeElapsed + mAllowedTimePerPeriodMs);
+ stats.sessionRateLimitExpirationTimeElapsed);
}
// Only schedule the alarm if:
// 1. There isn't one currently scheduled
@@ -1399,7 +1430,7 @@
ALARM_TAG_QUOTA_CHECK, alarmListener, mHandler);
alarmListener.setTriggerTime(inQuotaTimeElapsed);
} else if (DEBUG) {
- Slog.d(TAG, "No need to scheduling start alarm for " + pkgString);
+ Slog.d(TAG, "No need to schedule start alarm for " + pkgString);
}
}
@@ -1968,14 +1999,15 @@
private static final String KEY_MAX_JOB_COUNT_WORKING = "max_job_count_working";
private static final String KEY_MAX_JOB_COUNT_FREQUENT = "max_job_count_frequent";
private static final String KEY_MAX_JOB_COUNT_RARE = "max_job_count_rare";
- private static final String KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME =
- "max_count_per_allowed_time";
+ private static final String KEY_RATE_LIMITING_WINDOW_MS = "rate_limiting_window_ms";
+ private static final String KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW =
+ "max_job_count_per_rate_limiting_window";
private static final String KEY_MAX_SESSION_COUNT_ACTIVE = "max_session_count_active";
private static final String KEY_MAX_SESSION_COUNT_WORKING = "max_session_count_working";
private static final String KEY_MAX_SESSION_COUNT_FREQUENT = "max_session_count_frequent";
private static final String KEY_MAX_SESSION_COUNT_RARE = "max_session_count_rare";
- private static final String KEY_MAX_SESSION_COUNT_PER_ALLOWED_TIME =
- "max_session_count_per_allowed_time";
+ private static final String KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
+ "max_session_count_per_rate_limiting_window";
private static final String KEY_TIMING_SESSION_COALESCING_DURATION_MS =
"timing_session_coalescing_duration_ms";
@@ -1984,7 +2016,7 @@
private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
30 * 1000L; // 30 seconds
private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS =
- 10 * 60 * 1000L; // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_MS; // ACTIVE apps can run jobs at any time
private static final long DEFAULT_WINDOW_SIZE_WORKING_MS =
2 * 60 * 60 * 1000L; // 2 hours
private static final long DEFAULT_WINDOW_SIZE_FREQUENT_MS =
@@ -1992,16 +2024,18 @@
private static final long DEFAULT_WINDOW_SIZE_RARE_MS =
24 * 60 * 60 * 1000L; // 24 hours
private static final long DEFAULT_MAX_EXECUTION_TIME_MS =
- 4 * 60 * 60 * 1000L; // 4 hours
- private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE =
- 200; // 1200/hr
- private static final int DEFAULT_MAX_JOB_COUNT_WORKING =
- 1200; // 600/hr
- private static final int DEFAULT_MAX_JOB_COUNT_FREQUENT =
- 1800; // 225/hr
- private static final int DEFAULT_MAX_JOB_COUNT_RARE =
- 2400; // 100/hr
- private static final int DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME = 20;
+ 4 * HOUR_IN_MILLIS;
+ private static final long DEFAULT_RATE_LIMITING_WINDOW_MS =
+ 10 * MINUTE_IN_MILLIS;
+ private static final int DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = 20;
+ private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE = // 20/window = 120/hr = 1/session
+ DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
+ private static final int DEFAULT_MAX_JOB_COUNT_WORKING = // 120/window = 60/hr = 12/session
+ (int) (60.0 * DEFAULT_WINDOW_SIZE_WORKING_MS / HOUR_IN_MILLIS);
+ private static final int DEFAULT_MAX_JOB_COUNT_FREQUENT = // 200/window = 25/hr = 25/session
+ (int) (25.0 * DEFAULT_WINDOW_SIZE_FREQUENT_MS / HOUR_IN_MILLIS);
+ private static final int DEFAULT_MAX_JOB_COUNT_RARE = // 48/window = 2/hr = 16/session
+ (int) (2.0 * DEFAULT_WINDOW_SIZE_RARE_MS / HOUR_IN_MILLIS);
private static final int DEFAULT_MAX_SESSION_COUNT_ACTIVE =
20; // 120/hr
private static final int DEFAULT_MAX_SESSION_COUNT_WORKING =
@@ -2010,8 +2044,8 @@
8; // 1/hr
private static final int DEFAULT_MAX_SESSION_COUNT_RARE =
3; // .125/hr
- private static final int DEFAULT_MAX_SESSION_COUNT_PER_ALLOWED_TIME = 20;
- private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 0;
+ private static final int DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 20;
+ private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 5000; // 5 seconds
/** How much time each app will have to run jobs within their standby bucket window. */
public long ALLOWED_TIME_PER_PERIOD_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
@@ -2079,11 +2113,14 @@
*/
public int MAX_JOB_COUNT_RARE = DEFAULT_MAX_JOB_COUNT_RARE;
+ /** The period of time used to rate limit recently run jobs. */
+ public long RATE_LIMITING_WINDOW_MS = DEFAULT_RATE_LIMITING_WINDOW_MS;
+
/**
- * The maximum number of jobs that can run within the past
- * {@link #ALLOWED_TIME_PER_PERIOD_MS}.
+ * The maximum number of jobs that can run within the past {@link #RATE_LIMITING_WINDOW_MS}.
*/
- public int MAX_JOB_COUNT_PER_ALLOWED_TIME = DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME;
+ public int MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW =
+ DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
/**
* The maximum number of {@link TimingSession}s an app can run within this particular
@@ -2113,7 +2150,8 @@
* The maximum number of {@link TimingSession}s that can run within the past
* {@link #ALLOWED_TIME_PER_PERIOD_MS}.
*/
- public int MAX_SESSION_COUNT_PER_ALLOWED_TIME = DEFAULT_MAX_SESSION_COUNT_PER_ALLOWED_TIME;
+ public int MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
+ DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW;
/**
* Treat two distinct {@link TimingSession}s as the same if they start and end within this
@@ -2122,6 +2160,29 @@
public long TIMING_SESSION_COALESCING_DURATION_MS =
DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS;
+ // Safeguards
+
+ /** The minimum number of jobs that any bucket will be allowed to run within its window. */
+ private static final int MIN_BUCKET_JOB_COUNT = 10;
+
+ /**
+ * The minimum number of {@link TimingSession}s that any bucket will be allowed to run
+ * within its window.
+ */
+ private static final int MIN_BUCKET_SESSION_COUNT = 1;
+
+ /** The minimum value that {@link #MAX_EXECUTION_TIME_MS} can have. */
+ private static final long MIN_MAX_EXECUTION_TIME_MS = 60 * MINUTE_IN_MILLIS;
+
+ /** The minimum value that {@link #MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW} can have. */
+ private static final int MIN_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = 10;
+
+ /** The minimum value that {@link #MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW} can have. */
+ private static final int MIN_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 10;
+
+ /** The minimum value that {@link #RATE_LIMITING_WINDOW_MS} can have. */
+ private static final long MIN_RATE_LIMITING_WINDOW_MS = 30 * SECOND_IN_MILLIS;
+
QcConstants(Handler handler) {
super(handler);
}
@@ -2167,8 +2228,11 @@
KEY_MAX_JOB_COUNT_FREQUENT, DEFAULT_MAX_JOB_COUNT_FREQUENT);
MAX_JOB_COUNT_RARE = mParser.getInt(
KEY_MAX_JOB_COUNT_RARE, DEFAULT_MAX_JOB_COUNT_RARE);
- MAX_JOB_COUNT_PER_ALLOWED_TIME = mParser.getInt(
- KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME, DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME);
+ RATE_LIMITING_WINDOW_MS = mParser.getLong(
+ KEY_RATE_LIMITING_WINDOW_MS, DEFAULT_RATE_LIMITING_WINDOW_MS);
+ MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = mParser.getInt(
+ KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
+ DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW);
MAX_SESSION_COUNT_ACTIVE = mParser.getInt(
KEY_MAX_SESSION_COUNT_ACTIVE, DEFAULT_MAX_SESSION_COUNT_ACTIVE);
MAX_SESSION_COUNT_WORKING = mParser.getInt(
@@ -2177,9 +2241,9 @@
KEY_MAX_SESSION_COUNT_FREQUENT, DEFAULT_MAX_SESSION_COUNT_FREQUENT);
MAX_SESSION_COUNT_RARE = mParser.getInt(
KEY_MAX_SESSION_COUNT_RARE, DEFAULT_MAX_SESSION_COUNT_RARE);
- MAX_SESSION_COUNT_PER_ALLOWED_TIME = mParser.getInt(
- KEY_MAX_SESSION_COUNT_PER_ALLOWED_TIME,
- DEFAULT_MAX_SESSION_COUNT_PER_ALLOWED_TIME);
+ MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = mParser.getInt(
+ KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
+ DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
TIMING_SESSION_COALESCING_DURATION_MS = mParser.getLong(
KEY_TIMING_SESSION_COALESCING_DURATION_MS,
DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS);
@@ -2192,7 +2256,14 @@
synchronized (mLock) {
boolean changed = false;
- long newAllowedTimeMs = Math.min(MAX_PERIOD_MS,
+ long newMaxExecutionTimeMs = Math.max(MIN_MAX_EXECUTION_TIME_MS,
+ Math.min(MAX_PERIOD_MS, MAX_EXECUTION_TIME_MS));
+ if (mMaxExecutionTimeMs != newMaxExecutionTimeMs) {
+ mMaxExecutionTimeMs = newMaxExecutionTimeMs;
+ mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
+ changed = true;
+ }
+ long newAllowedTimeMs = Math.min(mMaxExecutionTimeMs,
Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_MS));
if (mAllowedTimePerPeriodMs != newAllowedTimeMs) {
mAllowedTimePerPeriodMs = newAllowedTimeMs;
@@ -2231,47 +2302,44 @@
mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
changed = true;
}
- long newMaxExecutionTimeMs = Math.max(60 * MINUTE_IN_MILLIS,
- Math.min(MAX_PERIOD_MS, MAX_EXECUTION_TIME_MS));
- if (mMaxExecutionTimeMs != newMaxExecutionTimeMs) {
- mMaxExecutionTimeMs = newMaxExecutionTimeMs;
- mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
+ long newRateLimitingWindowMs = Math.min(MAX_PERIOD_MS,
+ Math.max(MIN_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS));
+ if (mRateLimitingWindowMs != newRateLimitingWindowMs) {
+ mRateLimitingWindowMs = newRateLimitingWindowMs;
changed = true;
}
- int newMaxCountPerAllowedPeriod = Math.max(10,
- MAX_JOB_COUNT_PER_ALLOWED_TIME);
- if (mMaxJobCountPerAllowedTime != newMaxCountPerAllowedPeriod) {
- mMaxJobCountPerAllowedTime = newMaxCountPerAllowedPeriod;
+ int newMaxJobCountPerRateLimitingWindow = Math.max(
+ MIN_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
+ MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW);
+ if (mMaxJobCountPerRateLimitingWindow != newMaxJobCountPerRateLimitingWindow) {
+ mMaxJobCountPerRateLimitingWindow = newMaxJobCountPerRateLimitingWindow;
changed = true;
}
- int newActiveMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
- Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_ACTIVE));
+ int newActiveMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_ACTIVE);
if (mMaxBucketJobCounts[ACTIVE_INDEX] != newActiveMaxJobCount) {
mMaxBucketJobCounts[ACTIVE_INDEX] = newActiveMaxJobCount;
changed = true;
}
- int newWorkingMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
- Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_WORKING));
+ int newWorkingMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_WORKING);
if (mMaxBucketJobCounts[WORKING_INDEX] != newWorkingMaxJobCount) {
mMaxBucketJobCounts[WORKING_INDEX] = newWorkingMaxJobCount;
changed = true;
}
- int newFrequentMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
- Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_FREQUENT));
+ int newFrequentMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_FREQUENT);
if (mMaxBucketJobCounts[FREQUENT_INDEX] != newFrequentMaxJobCount) {
mMaxBucketJobCounts[FREQUENT_INDEX] = newFrequentMaxJobCount;
changed = true;
}
- int newRareMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
- Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_RARE));
+ int newRareMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_RARE);
if (mMaxBucketJobCounts[RARE_INDEX] != newRareMaxJobCount) {
mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
changed = true;
}
- int newMaxSessionCountPerAllowedPeriod = Math.max(10,
- MAX_SESSION_COUNT_PER_ALLOWED_TIME);
- if (mMaxSessionCountPerAllowedTime != newMaxSessionCountPerAllowedPeriod) {
- mMaxSessionCountPerAllowedTime = newMaxSessionCountPerAllowedPeriod;
+ int newMaxSessionCountPerRateLimitPeriod = Math.max(
+ MIN_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
+ MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
+ if (mMaxSessionCountPerRateLimitingWindow != newMaxSessionCountPerRateLimitPeriod) {
+ mMaxSessionCountPerRateLimitingWindow = newMaxSessionCountPerRateLimitPeriod;
changed = true;
}
int newActiveMaxSessionCount =
@@ -2332,14 +2400,15 @@
pw.printPair(KEY_MAX_JOB_COUNT_WORKING, MAX_JOB_COUNT_WORKING).println();
pw.printPair(KEY_MAX_JOB_COUNT_FREQUENT, MAX_JOB_COUNT_FREQUENT).println();
pw.printPair(KEY_MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE).println();
- pw.printPair(KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME, MAX_JOB_COUNT_PER_ALLOWED_TIME)
- .println();
+ pw.printPair(KEY_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS).println();
+ pw.printPair(KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
+ MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW).println();
pw.printPair(KEY_MAX_SESSION_COUNT_ACTIVE, MAX_SESSION_COUNT_ACTIVE).println();
pw.printPair(KEY_MAX_SESSION_COUNT_WORKING, MAX_SESSION_COUNT_WORKING).println();
pw.printPair(KEY_MAX_SESSION_COUNT_FREQUENT, MAX_SESSION_COUNT_FREQUENT).println();
pw.printPair(KEY_MAX_SESSION_COUNT_RARE, MAX_SESSION_COUNT_RARE).println();
- pw.printPair(KEY_MAX_SESSION_COUNT_PER_ALLOWED_TIME, MAX_SESSION_COUNT_PER_ALLOWED_TIME)
- .println();
+ pw.printPair(KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
+ MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW).println();
pw.printPair(KEY_TIMING_SESSION_COALESCING_DURATION_MS,
TIMING_SESSION_COALESCING_DURATION_MS).println();
pw.decreaseIndent();
@@ -2365,8 +2434,10 @@
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_FREQUENT,
MAX_JOB_COUNT_FREQUENT);
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE);
- proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_ALLOWED_TIME,
- MAX_JOB_COUNT_PER_ALLOWED_TIME);
+ proto.write(ConstantsProto.QuotaController.RATE_LIMITING_WINDOW_MS,
+ RATE_LIMITING_WINDOW_MS);
+ proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
+ MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW);
proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_ACTIVE,
MAX_SESSION_COUNT_ACTIVE);
proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_WORKING,
@@ -2375,8 +2446,8 @@
MAX_SESSION_COUNT_FREQUENT);
proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_RARE,
MAX_SESSION_COUNT_RARE);
- proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_PER_ALLOWED_TIME,
- MAX_SESSION_COUNT_PER_ALLOWED_TIME);
+ proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
+ MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
proto.write(ConstantsProto.QuotaController.TIMING_SESSION_COALESCING_DURATION_MS,
TIMING_SESSION_COALESCING_DURATION_MS);
proto.end(qcToken);
@@ -2431,8 +2502,18 @@
}
@VisibleForTesting
- int getMaxJobCountPerAllowedTime() {
- return mMaxJobCountPerAllowedTime;
+ int getMaxJobCountPerRateLimitingWindow() {
+ return mMaxJobCountPerRateLimitingWindow;
+ }
+
+ @VisibleForTesting
+ int getMaxSessionCountPerRateLimitingWindow() {
+ return mMaxSessionCountPerRateLimitingWindow;
+ }
+
+ @VisibleForTesting
+ long getRateLimitingWindowMs() {
+ return mRateLimitingWindowMs;
}
@VisibleForTesting
@@ -2441,11 +2522,6 @@
}
@VisibleForTesting
- int getMaxSessionCountPerAllowedTime() {
- return mMaxSessionCountPerAllowedTime;
- }
-
- @VisibleForTesting
@Nullable
List<TimingSession> getTimingSessions(int userId, String packageName) {
return mTimingSessions.get(userId, packageName);
@@ -2659,6 +2735,12 @@
StateControllerProto.QuotaController.ExecutionStats.WINDOW_SIZE_MS,
es.windowSizeMs);
proto.write(
+ StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_LIMIT,
+ es.jobCountLimit);
+ proto.write(
+ StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_LIMIT,
+ es.sessionCountLimit);
+ proto.write(
StateControllerProto.QuotaController.ExecutionStats.EXECUTION_TIME_IN_WINDOW_MS,
es.executionTimeInWindowMs);
proto.write(
@@ -2674,20 +2756,20 @@
StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_IN_WINDOW,
es.sessionCountInWindow);
proto.write(
- StateControllerProto.QuotaController.ExecutionStats.QUOTA_CUTOFF_TIME_ELAPSED,
- es.quotaCutoffTimeElapsed);
+ StateControllerProto.QuotaController.ExecutionStats.IN_QUOTA_TIME_ELAPSED,
+ es.inQuotaTimeElapsed);
proto.write(
StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_EXPIRATION_TIME_ELAPSED,
- es.jobCountExpirationTimeElapsed);
+ es.jobRateLimitExpirationTimeElapsed);
proto.write(
- StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_IN_ALLOWED_TIME,
- es.jobCountInAllowedTime);
+ StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_IN_RATE_LIMITING_WINDOW,
+ es.jobCountInRateLimitingWindow);
proto.write(
StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_EXPIRATION_TIME_ELAPSED,
- es.sessionCountExpirationTimeElapsed);
+ es.sessionRateLimitExpirationTimeElapsed);
proto.write(
- StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_IN_ALLOWED_TIME,
- es.sessionCountInAllowedTime);
+ StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_IN_RATE_LIMITING_WINDOW,
+ es.sessionCountInRateLimitingWindow);
proto.end(esToken);
}
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsAccess.java b/services/core/java/com/android/server/net/NetworkStatsAccess.java
index cebc472..7c1c1c7 100644
--- a/services/core/java/com/android/server/net/NetworkStatsAccess.java
+++ b/services/core/java/com/android/server/net/NetworkStatsAccess.java
@@ -109,7 +109,7 @@
final TelephonyManager tm = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
boolean hasCarrierPrivileges = tm != null &&
- tm.checkCarrierPrivilegesForPackage(callingPackage) ==
+ tm.checkCarrierPrivilegesForPackageAnyPhone(callingPackage) ==
TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
boolean isDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid,
DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index 69efd02..473cc97 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -263,6 +263,10 @@
return stats;
}
+ /**
+ * @deprecated Use NetworkStatsService#getDetailedUidStats which also accounts for
+ * VPN traffic
+ */
public NetworkStats readNetworkStatsDetail() throws IOException {
return readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null);
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index a2e7e0c..bdff500 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -41,10 +41,10 @@
import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
-import libcore.io.IoUtils;
-
import com.google.android.collect.Sets;
+import libcore.io.IoUtils;
+
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
@@ -234,7 +234,7 @@
if (vpnArray != null) {
for (VpnInfo info : vpnArray) {
- delta.migrateTun(info.ownerUid, info.vpnIface, info.primaryUnderlyingIface);
+ delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces);
}
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index f34ace5..a13368f 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -293,6 +293,22 @@
/** Data layer operation counters for splicing into other structures. */
private NetworkStats mUidOperations = new NetworkStats(0L, 10);
+ /**
+ * Snapshot containing most recent network stats for all UIDs across all interfaces and tags
+ * since boot.
+ *
+ * <p>Maintains migrated VPN stats which are result of performing TUN migration on {@link
+ * #mLastUidDetailSnapshot}.
+ */
+ @GuardedBy("mStatsLock")
+ private NetworkStats mTunAdjustedStats;
+ /**
+ * Used by {@link #mTunAdjustedStats} to migrate VPN traffic over delta between this snapshot
+ * and latest snapshot.
+ */
+ @GuardedBy("mStatsLock")
+ private NetworkStats mLastUidDetailSnapshot;
+
/** Must be set in factory by calling #setHandler. */
private Handler mHandler;
private Handler.Callback mHandlerCallback;
@@ -812,15 +828,39 @@
@Override
public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
try {
+ // Get the latest snapshot from NetworkStatsFactory.
+ // TODO: Querying for INTERFACES_ALL may incur performance penalty. Consider restricting
+ // this to limited set of ifaces.
+ NetworkStats uidDetailStats = getNetworkStatsUidDetail(INTERFACES_ALL);
+
+ // Migrate traffic from VPN UID over delta and update mTunAdjustedStats.
+ NetworkStats result;
+ synchronized (mStatsLock) {
+ migrateTunTraffic(uidDetailStats, mVpnInfos);
+ result = mTunAdjustedStats.clone();
+ }
+
+ // Apply filter based on ifacesToQuery.
final String[] ifacesToQuery =
NetworkStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
- return getNetworkStatsUidDetail(ifacesToQuery);
+ result.filter(UID_ALL, ifacesToQuery, TAG_ALL);
+ return result;
} catch (RemoteException e) {
Log.wtf(TAG, "Error compiling UID stats", e);
return new NetworkStats(0L, 0);
}
}
+ @VisibleForTesting
+ NetworkStats getTunAdjustedStats() {
+ synchronized (mStatsLock) {
+ if (mTunAdjustedStats == null) {
+ return null;
+ }
+ return mTunAdjustedStats.clone();
+ }
+ }
+
@Override
public String[] getMobileIfaces() {
return mMobileIfaces;
@@ -1295,6 +1335,34 @@
// a race condition between the service handler thread and the observer's
mStatsObservers.updateStats(xtSnapshot, uidSnapshot, new ArrayMap<>(mActiveIfaces),
new ArrayMap<>(mActiveUidIfaces), vpnArray, currentTime);
+
+ migrateTunTraffic(uidSnapshot, vpnArray);
+ }
+
+ /**
+ * Updates {@link #mTunAdjustedStats} with the delta containing traffic migrated off of VPNs.
+ */
+ @GuardedBy("mStatsLock")
+ private void migrateTunTraffic(NetworkStats uidDetailStats, VpnInfo[] vpnInfoArray) {
+ if (mTunAdjustedStats == null) {
+ // Either device booted or system server restarted, hence traffic cannot be migrated
+ // correctly without knowing the past state of VPN's underlying networks.
+ mTunAdjustedStats = uidDetailStats;
+ mLastUidDetailSnapshot = uidDetailStats;
+ return;
+ }
+ // Migrate delta traffic from VPN to other apps.
+ NetworkStats delta = uidDetailStats.subtract(mLastUidDetailSnapshot);
+ for (VpnInfo info : vpnInfoArray) {
+ delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces);
+ }
+ // Filter out debug entries as that may lead to over counting.
+ delta.filterDebugEntries();
+ // Update #mTunAdjustedStats with migrated delta.
+ mTunAdjustedStats.combineAllValues(delta);
+ mTunAdjustedStats.setElapsedRealtime(uidDetailStats.getElapsedRealtime());
+ // Update last snapshot.
+ mLastUidDetailSnapshot = uidDetailStats;
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2a61fee..d35f952 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17080,6 +17080,13 @@
cleanUpAppIdCreation(result);
}
}
+ // TODO(patb): create a more descriptive reason than unknown in future release
+ // mark all non-failure installs as UNKNOWN so we do not treat them as success
+ for (InstallRequest request : requests) {
+ if (request.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ request.installResult.returnCode = PackageManager.INSTALL_UNKNOWN;
+ }
+ }
}
for (PrepareResult result : prepareResults.values()) {
if (result.freezer != null) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index f5c8049..81de8e2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2496,9 +2496,9 @@
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
- if (replaceExisting) {
- sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
- }
+ }
+ if (replaceExisting) {
+ sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
}
return params;
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index eec4b70..d6e87aa 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -674,9 +674,10 @@
return new ArrayList<>();
}
- // Get the list of all dynamic shortcuts in this package
+ // Get the list of all dynamic shortcuts in this package.
final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
- findAll(shortcuts, ShortcutInfo::isDynamicVisible, ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+ findAll(shortcuts, ShortcutInfo::isDynamicVisible,
+ ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION);
final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
for (int i = 0; i < shortcuts.size(); i++) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index e2bbb2d..cfd3ae6 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2085,7 +2085,8 @@
nextTimeout = -1;
}
- if ((mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0) {
+ if ((mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
+ && (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) == 0) {
nextTimeout = mAttentionDetector.updateUserActivity(nextTimeout);
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 476a273..165055a 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -19,6 +19,7 @@
import android.content.pm.PackageInfo;
import android.os.AsyncTask;
import android.os.UserHandle;
+import android.util.Slog;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
@@ -154,7 +155,10 @@
WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
if (fallbackProvider != null) {
+ Slog.i(TAG, "One-time migration: enabling " + fallbackProvider.packageName);
mSystemInterface.enablePackageForAllUsers(mContext, fallbackProvider.packageName, true);
+ } else {
+ Slog.i(TAG, "Skipping one-time migration: no fallback provider");
}
mSystemInterface.enableFallbackLogic(false);
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index c5b2566..434239f 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -231,6 +231,11 @@
mService.mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded();
}
+ // Once the target is shown, prevent spurious background app switches
+ if (reorderMode == REORDER_MOVE_TO_TOP) {
+ mService.stopAppSwitches();
+ }
+
mService.mH.post(
() -> mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, false));
diff --git a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
index 854537b..fb781b0 100644
--- a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
@@ -113,7 +113,12 @@
// statistics because it passes every touch event though a GestureDetector. By creating an
// anonymous subclass of GestureDetector, these statistics will be recorded with a unique
// source name that can be filtered.
- mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), mHandler) {};
+
+ // GestureDetector would get a ViewConfiguration instance by context, that may also
+ // create a new WindowManagerImpl for the new display, and lock WindowManagerGlobal
+ // temporarily in the constructor that would make a deadlock.
+ mHandler.post(() -> mGestureDetector =
+ new GestureDetector(mContext, new FlingGestureDetector(), mHandler) {});
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 4e89357..1db6b8e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -386,6 +386,8 @@
ExecutionStats expectedStats = new ExecutionStats();
expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
final int uid = 10001;
mQuotaController.onAppRemovedLocked("com.android.test.remove", uid);
@@ -424,6 +426,8 @@
ExecutionStats expectedStats = new ExecutionStats();
expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
mQuotaController.onUserRemovedLocked(0);
assertNull(mQuotaController.getTimingSessions(0, "com.android.test"));
@@ -456,6 +460,8 @@
ExecutionStats inputStats = new ExecutionStats();
inputStats.windowSizeMs = expectedStats.windowSizeMs = 12 * HOUR_IN_MILLIS;
+ inputStats.jobCountLimit = expectedStats.jobCountLimit = 100;
+ inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 100;
// Invalid time is now +24 hours since there are no sessions at all for the app.
expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
mQuotaController.updateExecutionStatsLocked(0, "com.android.test.not.run", inputStats);
@@ -520,6 +526,7 @@
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = HOUR_IN_MILLIS;
+ inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 2;
// Invalid time is now since the start of the session is at the very edge of the window
// cutoff time.
expectedStats.expirationTimeElapsed = now;
@@ -528,10 +535,13 @@
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 15;
expectedStats.sessionCountInWindow = 3;
+ expectedStats.inQuotaTimeElapsed = now + 11 * MINUTE_IN_MILLIS;
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
+ inputStats.jobCountLimit = expectedStats.jobCountLimit = 6;
+ inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 100;
// Invalid time is now since the session straddles the window cutoff time.
expectedStats.expirationTimeElapsed = now;
expectedStats.executionTimeInWindowMs = 11 * MINUTE_IN_MILLIS;
@@ -539,8 +549,7 @@
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 15;
expectedStats.sessionCountInWindow = 4;
- expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
- + mQcConstants.IN_QUOTA_BUFFER_MS;
+ expectedStats.inQuotaTimeElapsed = now + 5 * MINUTE_IN_MILLIS;
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
assertEquals(expectedStats, inputStats);
@@ -553,8 +562,9 @@
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 15;
expectedStats.sessionCountInWindow = 4;
- expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
- + mQcConstants.IN_QUOTA_BUFFER_MS;
+ // App goes under job execution time limit in ~61 minutes, but will be under job count limit
+ // in 65 minutes.
+ expectedStats.inQuotaTimeElapsed = now + 65 * MINUTE_IN_MILLIS;
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
assertEquals(expectedStats, inputStats);
@@ -567,8 +577,7 @@
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 15;
expectedStats.sessionCountInWindow = 5;
- expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
- + mQcConstants.IN_QUOTA_BUFFER_MS;
+ expectedStats.inQuotaTimeElapsed = now + 4 * HOUR_IN_MILLIS + 5 * MINUTE_IN_MILLIS;
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
assertEquals(expectedStats, inputStats);
@@ -577,6 +586,7 @@
.add(0,
createTimingSession(now - (23 * HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 3));
inputStats.windowSizeMs = expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
+ inputStats.jobCountLimit = expectedStats.jobCountLimit = 100;
// Invalid time is now +1 hour since the earliest session in the max period is 1 hour
// before the end of the max period cutoff time.
expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
@@ -585,7 +595,7 @@
expectedStats.executionTimeInMaxPeriodMs = 23 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 18;
expectedStats.sessionCountInWindow = 5;
- expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
+ expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS
+ mQcConstants.IN_QUOTA_BUFFER_MS;
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
assertEquals(expectedStats, inputStats);
@@ -602,7 +612,7 @@
expectedStats.executionTimeInMaxPeriodMs = 24 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 20;
expectedStats.sessionCountInWindow = 5;
- expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
+ expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS
+ mQcConstants.IN_QUOTA_BUFFER_MS;
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
assertEquals(expectedStats, inputStats);
@@ -627,6 +637,8 @@
// Active
expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
expectedStats.expirationTimeElapsed = now + 4 * MINUTE_IN_MILLIS;
expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 5;
@@ -638,45 +650,103 @@
// Working
expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING;
expectedStats.expirationTimeElapsed = now;
expectedStats.executionTimeInWindowMs = 13 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 10;
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 20;
expectedStats.sessionCountInWindow = 2;
- expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
+ expectedStats.inQuotaTimeElapsed = now + 3 * MINUTE_IN_MILLIS
+ mQcConstants.IN_QUOTA_BUFFER_MS;
assertEquals(expectedStats,
mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX));
// Frequent
expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT;
expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
expectedStats.executionTimeInWindowMs = 23 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 15;
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 20;
expectedStats.sessionCountInWindow = 3;
- expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
+ expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS
+ mQcConstants.IN_QUOTA_BUFFER_MS;
assertEquals(expectedStats,
mQuotaController.getExecutionStatsLocked(0, "com.android.test", FREQUENT_INDEX));
// Rare
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
expectedStats.executionTimeInWindowMs = 33 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 20;
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 20;
expectedStats.sessionCountInWindow = 4;
- expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
+ expectedStats.inQuotaTimeElapsed = now + 22 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS
+ mQcConstants.IN_QUOTA_BUFFER_MS;
assertEquals(expectedStats,
mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
}
/**
+ * Tests that getExecutionStatsLocked returns the correct stats soon after device startup.
+ */
+ @Test
+ public void testGetExecutionStatsLocked_Values_BeginningOfTime() {
+ // Set time to 3 minutes after boot.
+ advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
+ advanceElapsedClock(3 * MINUTE_IN_MILLIS);
+
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2));
+
+ ExecutionStats expectedStats = new ExecutionStats();
+
+ // Active
+ expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+ expectedStats.expirationTimeElapsed = 11 * MINUTE_IN_MILLIS;
+ expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInWindow = 2;
+ expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInMaxPeriod = 2;
+ expectedStats.sessionCountInWindow = 1;
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX));
+
+ // Working
+ expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING;
+ expectedStats.expirationTimeElapsed = 2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX));
+
+ // Frequent
+ expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT;
+ expectedStats.expirationTimeElapsed = 8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test", FREQUENT_INDEX));
+
+ // Rare
+ expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
+ expectedStats.expirationTimeElapsed = 24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
+ }
+
+ /**
* Tests that getExecutionStatsLocked returns the correct timing session stats when coalescing.
*/
@Test
@@ -850,13 +920,15 @@
ExecutionStats expectedStats = new ExecutionStats();
expectedStats.windowSizeMs = originalStatsActive.windowSizeMs;
+ expectedStats.jobCountLimit = originalStatsActive.jobCountLimit;
+ expectedStats.sessionCountLimit = originalStatsActive.sessionCountLimit;
expectedStats.expirationTimeElapsed = originalStatsActive.expirationTimeElapsed;
expectedStats.executionTimeInWindowMs = originalStatsActive.executionTimeInWindowMs;
expectedStats.bgJobCountInWindow = originalStatsActive.bgJobCountInWindow;
expectedStats.executionTimeInMaxPeriodMs = originalStatsActive.executionTimeInMaxPeriodMs;
expectedStats.bgJobCountInMaxPeriod = originalStatsActive.bgJobCountInMaxPeriod;
expectedStats.sessionCountInWindow = originalStatsActive.sessionCountInWindow;
- expectedStats.quotaCutoffTimeElapsed = originalStatsActive.quotaCutoffTimeElapsed;
+ expectedStats.inQuotaTimeElapsed = originalStatsActive.inQuotaTimeElapsed;
final ExecutionStats newStatsActive = mQuotaController.getExecutionStatsLocked(0,
"com.android.test", ACTIVE_INDEX);
// Stats for the same bucket should use the same object.
@@ -864,33 +936,39 @@
assertEquals(expectedStats, newStatsActive);
expectedStats.windowSizeMs = originalStatsWorking.windowSizeMs;
+ expectedStats.jobCountLimit = originalStatsWorking.jobCountLimit;
+ expectedStats.sessionCountLimit = originalStatsWorking.sessionCountLimit;
expectedStats.expirationTimeElapsed = originalStatsWorking.expirationTimeElapsed;
expectedStats.executionTimeInWindowMs = originalStatsWorking.executionTimeInWindowMs;
expectedStats.bgJobCountInWindow = originalStatsWorking.bgJobCountInWindow;
expectedStats.sessionCountInWindow = originalStatsWorking.sessionCountInWindow;
- expectedStats.quotaCutoffTimeElapsed = originalStatsWorking.quotaCutoffTimeElapsed;
+ expectedStats.inQuotaTimeElapsed = originalStatsWorking.inQuotaTimeElapsed;
final ExecutionStats newStatsWorking = mQuotaController.getExecutionStatsLocked(0,
"com.android.test", WORKING_INDEX);
assertTrue(originalStatsWorking == newStatsWorking);
assertNotEquals(expectedStats, newStatsWorking);
expectedStats.windowSizeMs = originalStatsFrequent.windowSizeMs;
+ expectedStats.jobCountLimit = originalStatsFrequent.jobCountLimit;
+ expectedStats.sessionCountLimit = originalStatsFrequent.sessionCountLimit;
expectedStats.expirationTimeElapsed = originalStatsFrequent.expirationTimeElapsed;
expectedStats.executionTimeInWindowMs = originalStatsFrequent.executionTimeInWindowMs;
expectedStats.bgJobCountInWindow = originalStatsFrequent.bgJobCountInWindow;
expectedStats.sessionCountInWindow = originalStatsFrequent.sessionCountInWindow;
- expectedStats.quotaCutoffTimeElapsed = originalStatsFrequent.quotaCutoffTimeElapsed;
+ expectedStats.inQuotaTimeElapsed = originalStatsFrequent.inQuotaTimeElapsed;
final ExecutionStats newStatsFrequent = mQuotaController.getExecutionStatsLocked(0,
"com.android.test", FREQUENT_INDEX);
assertTrue(originalStatsFrequent == newStatsFrequent);
assertNotEquals(expectedStats, newStatsFrequent);
expectedStats.windowSizeMs = originalStatsRare.windowSizeMs;
+ expectedStats.jobCountLimit = originalStatsRare.jobCountLimit;
+ expectedStats.sessionCountLimit = originalStatsRare.sessionCountLimit;
expectedStats.expirationTimeElapsed = originalStatsRare.expirationTimeElapsed;
expectedStats.executionTimeInWindowMs = originalStatsRare.executionTimeInWindowMs;
expectedStats.bgJobCountInWindow = originalStatsRare.bgJobCountInWindow;
expectedStats.sessionCountInWindow = originalStatsRare.sessionCountInWindow;
- expectedStats.quotaCutoffTimeElapsed = originalStatsRare.quotaCutoffTimeElapsed;
+ expectedStats.inQuotaTimeElapsed = originalStatsRare.inQuotaTimeElapsed;
final ExecutionStats newStatsRare = mQuotaController.getExecutionStatsLocked(0,
"com.android.test", RARE_INDEX);
assertTrue(originalStatsRare == newStatsRare);
@@ -1065,7 +1143,7 @@
public void testIsWithinQuotaLocked_UnderDuration_OverJobCount() {
setDischarging();
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME;
+ final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
mQuotaController.saveTimingSession(0, "com.android.test.spam",
createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25));
mQuotaController.saveTimingSession(0, "com.android.test.spam",
@@ -1100,7 +1178,7 @@
public void testIsWithinQuotaLocked_OverDuration_OverJobCount() {
setDischarging();
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME;
+ final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25));
mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1141,7 +1219,7 @@
advanceElapsedClock(10 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS);
assertEquals(2, mQuotaController.getExecutionStatsLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX).jobCountInAllowedTime);
+ SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX).jobCountInRateLimitingWindow);
assertTrue(mQuotaController.isWithinQuotaLocked(jobStatus));
}
@@ -1212,9 +1290,9 @@
mQuotaController.getTimingSessions(SOURCE_USER_ID, fgChangerPkgName).size());
for (int i = ACTIVE_INDEX; i < RARE_INDEX; ++i) {
assertEquals(42, mQuotaController.getExecutionStatsLocked(
- SOURCE_USER_ID, fgChangerPkgName, i).jobCountInAllowedTime);
+ SOURCE_USER_ID, fgChangerPkgName, i).jobCountInRateLimitingWindow);
assertEquals(1, mQuotaController.getExecutionStatsLocked(
- SOURCE_USER_ID, unaffectedPkgName, i).jobCountInAllowedTime);
+ SOURCE_USER_ID, unaffectedPkgName, i).jobCountInRateLimitingWindow);
}
}
@@ -1555,26 +1633,29 @@
}
@Test
- public void testMaybeScheduleStartAlarmLocked_JobCount_AllowedTime() {
+ public void testMaybeScheduleStartAlarmLocked_JobCount_RateLimitingWindow() {
+ // Set rate limiting period different from allowed time to confirm code sets based on
+ // the former.
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 10 * MINUTE_IN_MILLIS;
+ mQcConstants.RATE_LIMITING_WINDOW_MS = 5 * MINUTE_IN_MILLIS;
+ mQcConstants.updateConstants();
+
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
final int standbyBucket = WORKING_INDEX;
ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
SOURCE_PACKAGE, standbyBucket);
- stats.jobCountInAllowedTime =
- mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME + 2;
+ stats.jobCountInRateLimitingWindow =
+ mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW + 2;
// Invalid time in the past, so the count shouldn't be used.
- stats.jobCountExpirationTimeElapsed =
- now - mQuotaController.getAllowedTimePerPeriodMs() / 2;
+ stats.jobRateLimitExpirationTimeElapsed = now - 5 * MINUTE_IN_MILLIS / 2;
mQuotaController.maybeScheduleStartAlarmLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
- // Invalid time in the future, so the count should be used.
- stats.jobCountExpirationTimeElapsed =
- now + mQuotaController.getAllowedTimePerPeriodMs() / 2;
- final long expectedWorkingAlarmTime =
- stats.jobCountExpirationTimeElapsed + mQuotaController.getAllowedTimePerPeriodMs();
+ // Valid time in the future, so the count should be used.
+ stats.jobRateLimitExpirationTimeElapsed = now + 5 * MINUTE_IN_MILLIS / 2;
+ final long expectedWorkingAlarmTime = stats.jobRateLimitExpirationTimeElapsed;
mQuotaController.maybeScheduleStartAlarmLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
verify(mAlarmManager, times(1))
@@ -1732,12 +1813,13 @@
mQcConstants.MAX_JOB_COUNT_WORKING = 4000;
mQcConstants.MAX_JOB_COUNT_FREQUENT = 3000;
mQcConstants.MAX_JOB_COUNT_RARE = 2000;
- mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 500;
+ mQcConstants.RATE_LIMITING_WINDOW_MS = 15 * MINUTE_IN_MILLIS;
+ mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = 500;
mQcConstants.MAX_SESSION_COUNT_ACTIVE = 500;
mQcConstants.MAX_SESSION_COUNT_WORKING = 400;
mQcConstants.MAX_SESSION_COUNT_FREQUENT = 300;
mQcConstants.MAX_SESSION_COUNT_RARE = 200;
- mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME = 50;
+ mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 50;
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 10 * SECOND_IN_MILLIS;
mQcConstants.updateConstants();
@@ -1750,12 +1832,13 @@
mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
assertEquals(3 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
- assertEquals(500, mQuotaController.getMaxJobCountPerAllowedTime());
+ assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
+ assertEquals(500, mQuotaController.getMaxJobCountPerRateLimitingWindow());
assertEquals(5000, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
assertEquals(4000, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
assertEquals(3000, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
- assertEquals(50, mQuotaController.getMaxSessionCountPerAllowedTime());
+ assertEquals(50, mQuotaController.getMaxSessionCountPerRateLimitingWindow());
assertEquals(500, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
assertEquals(400, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
assertEquals(300, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
@@ -1778,12 +1861,13 @@
mQcConstants.MAX_JOB_COUNT_WORKING = 1;
mQcConstants.MAX_JOB_COUNT_FREQUENT = 1;
mQcConstants.MAX_JOB_COUNT_RARE = 1;
- mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 0;
+ mQcConstants.RATE_LIMITING_WINDOW_MS = 15 * SECOND_IN_MILLIS;
+ mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = 0;
mQcConstants.MAX_SESSION_COUNT_ACTIVE = -1;
- mQcConstants.MAX_SESSION_COUNT_WORKING = 1;
- mQcConstants.MAX_SESSION_COUNT_FREQUENT = 2;
- mQcConstants.MAX_SESSION_COUNT_RARE = 1;
- mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME = 0;
+ mQcConstants.MAX_SESSION_COUNT_WORKING = 0;
+ mQcConstants.MAX_SESSION_COUNT_FREQUENT = -3;
+ mQcConstants.MAX_SESSION_COUNT_RARE = 0;
+ mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 0;
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = -1;
mQcConstants.updateConstants();
@@ -1795,16 +1879,17 @@
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
assertEquals(HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
- assertEquals(10, mQuotaController.getMaxJobCountPerAllowedTime());
- assertEquals(100, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
- assertEquals(100, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
- assertEquals(100, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
- assertEquals(100, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
- assertEquals(10, mQuotaController.getMaxSessionCountPerAllowedTime());
- assertEquals(3, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
- assertEquals(3, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
- assertEquals(3, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
- assertEquals(3, mQuotaController.getBucketMaxSessionCounts()[RARE_INDEX]);
+ assertEquals(30 * SECOND_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
+ assertEquals(10, mQuotaController.getMaxJobCountPerRateLimitingWindow());
+ assertEquals(10, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
+ assertEquals(10, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
+ assertEquals(10, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
+ assertEquals(10, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
+ assertEquals(10, mQuotaController.getMaxSessionCountPerRateLimitingWindow());
+ assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
+ assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
+ assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
+ assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[RARE_INDEX]);
assertEquals(0, mQuotaController.getTimingSessionCoalescingDurationMs());
// Test larger than a day. Controller should cap at one day.
@@ -1815,6 +1900,7 @@
mQcConstants.WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS;
mQcConstants.WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS;
mQcConstants.MAX_EXECUTION_TIME_MS = 25 * HOUR_IN_MILLIS;
+ mQcConstants.RATE_LIMITING_WINDOW_MS = 25 * HOUR_IN_MILLIS;
mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 25 * HOUR_IN_MILLIS;
mQcConstants.updateConstants();
@@ -1826,6 +1912,7 @@
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
assertEquals(15 * MINUTE_IN_MILLIS,
mQuotaController.getTimingSessionCoalescingDurationMs());
}
@@ -2126,7 +2213,7 @@
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
SOURCE_PACKAGE, standbyBucket);
- assertEquals(0, stats.jobCountInAllowedTime);
+ assertEquals(0, stats.jobCountInRateLimitingWindow);
setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
mQuotaController.prepareForExecutionLocked(jobFg1);
@@ -2138,7 +2225,7 @@
mQuotaController.maybeStopTrackingJobLocked(jobFg2, null, false);
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(0, stats.jobCountInAllowedTime);
+ assertEquals(0, stats.jobCountInRateLimitingWindow);
}
/**
@@ -2154,7 +2241,7 @@
ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
SOURCE_PACKAGE, standbyBucket);
- assertEquals(0, stats.jobCountInAllowedTime);
+ assertEquals(0, stats.jobCountInRateLimitingWindow);
setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
mQuotaController.prepareForExecutionLocked(jobBg1);
@@ -2165,7 +2252,7 @@
advanceElapsedClock(10 * SECOND_IN_MILLIS);
mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
- assertEquals(2, stats.jobCountInAllowedTime);
+ assertEquals(2, stats.jobCountInRateLimitingWindow);
}
/**
@@ -2421,10 +2508,10 @@
/**
* Tests that the start alarm is properly scheduled when a job has been throttled due to the job
- * count quota.
+ * count rate limiting.
*/
@Test
- public void testStartAlarmScheduled_JobCount_AllowedTime() {
+ public void testStartAlarmScheduled_JobCount_RateLimitingWindow() {
// saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
// because it schedules an alarm too. Prevent it from doing so.
spyOn(mQuotaController);
@@ -2432,7 +2519,7 @@
// Essentially disable session throttling.
mQcConstants.MAX_SESSION_COUNT_WORKING =
- mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME = Integer.MAX_VALUE;
+ mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = Integer.MAX_VALUE;
mQcConstants.updateConstants();
final int standbyBucket = WORKING_INDEX;
@@ -2444,7 +2531,7 @@
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Ran jobs up to the job limit. All of them should be allowed to run.
- for (int i = 0; i < mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME; ++i) {
+ for (int i = 0; i < mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; ++i) {
JobStatus job = createJobStatus("testStartAlarmScheduled_JobCount_AllowedTime", i);
setStandbyBucket(WORKING_INDEX, job);
mQuotaController.maybeStartTrackingJobLocked(job, null);
@@ -2466,18 +2553,17 @@
ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
SOURCE_PACKAGE, standbyBucket);
- final long expectedWorkingAlarmTime =
- stats.jobCountExpirationTimeElapsed + mQcConstants.ALLOWED_TIME_PER_PERIOD_MS;
+ final long expectedWorkingAlarmTime = stats.jobRateLimitExpirationTimeElapsed;
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
}
/**
- * Tests that the start alarm is properly scheduled when a job has been throttled due to the job
- * count quota.
+ * Tests that the start alarm is properly scheduled when a job has been throttled due to the
+ * session count rate limiting.
*/
@Test
- public void testStartAlarmScheduled_TimingSessionCount_AllowedTime() {
+ public void testStartAlarmScheduled_TimingSessionCount_RateLimitingWindow() {
// saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
// because it schedules an alarm too. Prevent it from doing so.
spyOn(mQuotaController);
@@ -2485,10 +2571,10 @@
// Essentially disable job count throttling.
mQcConstants.MAX_JOB_COUNT_FREQUENT =
- mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = Integer.MAX_VALUE;
- // Make sure throttling is because of COUNT_PER_ALLOWED_TIME.
+ mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = Integer.MAX_VALUE;
+ // Make sure throttling is because of COUNT_PER_RATE_LIMITING_WINDOW.
mQcConstants.MAX_SESSION_COUNT_FREQUENT =
- mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME + 1;
+ mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW + 1;
mQcConstants.updateConstants();
final int standbyBucket = FREQUENT_INDEX;
@@ -2500,7 +2586,7 @@
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Ran jobs up to the job limit. All of them should be allowed to run.
- for (int i = 0; i < mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME; ++i) {
+ for (int i = 0; i < mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW; ++i) {
JobStatus job = createJobStatus(
"testStartAlarmScheduled_TimingSessionCount_AllowedTime", i);
setStandbyBucket(FREQUENT_INDEX, job);
@@ -2515,7 +2601,7 @@
// Start alarm shouldn't have been scheduled since the app was in quota up until this point.
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
- // The app is now out of job count quota
+ // The app is now out of session count quota
JobStatus throttledJob = createJobStatus(
"testStartAlarmScheduled_TimingSessionCount_AllowedTime", 42);
mQuotaController.maybeStartTrackingJobLocked(throttledJob, null);
@@ -2523,8 +2609,7 @@
ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
SOURCE_PACKAGE, standbyBucket);
- final long expectedWorkingAlarmTime =
- stats.sessionCountExpirationTimeElapsed + mQcConstants.ALLOWED_TIME_PER_PERIOD_MS;
+ final long expectedWorkingAlarmTime = stats.sessionRateLimitExpirationTimeElapsed;
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index fa1bcac..fd3678d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -423,9 +423,7 @@
assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
assertEquals(null, si.getIntent());
assertEquals(123, si.getRank());
- assertEquals("person", si.getPersons()[0].getName());
- assertEquals("personKey", si.getPersons()[0].getKey());
- assertEquals("personUri", si.getPersons()[0].getUri());
+ assertEquals(null, si.getPersons());
assertEquals(1, si.getExtras().getInt("k"));
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
@@ -455,6 +453,30 @@
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION);
+
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
+ assertEquals(null, si.getIcon());
+ assertEquals("title", si.getTitle());
+ assertEquals("text", si.getText());
+ assertEquals("dismes", si.getDisabledMessage());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(123, si.getRank());
+ assertEquals("person", si.getPersons()[0].getName());
+ assertEquals("personKey", si.getPersons()[0].getKey());
+ assertEquals("personUri", si.getPersons()[0].getUri());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
+ assertEquals(null, si.getBitmapPath());
+
+ assertEquals(456, si.getIconResourceId());
+ assertEquals(null, si.getIconResName());
}
public void testShortcutInfoClone_resId() {
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 984d8f7..5d39a2c 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -341,6 +341,7 @@
private String mOperatorAlphaLongRaw;
private String mOperatorAlphaShortRaw;
+ private boolean mIsIwlanPreferred;
/**
* get String description of roaming type
@@ -427,6 +428,7 @@
mNrFrequencyRange = s.mNrFrequencyRange;
mOperatorAlphaLongRaw = s.mOperatorAlphaLongRaw;
mOperatorAlphaShortRaw = s.mOperatorAlphaShortRaw;
+ mIsIwlanPreferred = s.mIsIwlanPreferred;
}
/**
@@ -463,6 +465,7 @@
mNrFrequencyRange = in.readInt();
mOperatorAlphaLongRaw = in.readString();
mOperatorAlphaShortRaw = in.readString();
+ mIsIwlanPreferred = in.readBoolean();
}
public void writeToParcel(Parcel out, int flags) {
@@ -492,6 +495,7 @@
out.writeInt(mNrFrequencyRange);
out.writeString(mOperatorAlphaLongRaw);
out.writeString(mOperatorAlphaShortRaw);
+ out.writeBoolean(mIsIwlanPreferred);
}
public int describeContents() {
@@ -853,7 +857,8 @@
mNetworkRegistrationInfos,
mNrFrequencyRange,
mOperatorAlphaLongRaw,
- mOperatorAlphaShortRaw);
+ mOperatorAlphaShortRaw,
+ mIsIwlanPreferred);
}
}
@@ -885,7 +890,8 @@
&& equalsHandlesNulls(mOperatorAlphaShortRaw, s.mOperatorAlphaShortRaw)
&& mNetworkRegistrationInfos.size() == s.mNetworkRegistrationInfos.size()
&& mNetworkRegistrationInfos.containsAll(s.mNetworkRegistrationInfos)
- && mNrFrequencyRange == s.mNrFrequencyRange;
+ && mNrFrequencyRange == s.mNrFrequencyRange
+ && mIsIwlanPreferred == s.mIsIwlanPreferred;
}
}
@@ -1043,6 +1049,7 @@
.append(", mNrFrequencyRange=").append(mNrFrequencyRange)
.append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw)
.append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw)
+ .append(", mIsIwlanPreferred=").append(mIsIwlanPreferred)
.append("}").toString();
}
}
@@ -1085,6 +1092,7 @@
}
mOperatorAlphaLongRaw = null;
mOperatorAlphaShortRaw = null;
+ mIsIwlanPreferred = false;
}
public void setStateOutOfService() {
@@ -1459,20 +1467,9 @@
/** @hide */
@UnsupportedAppUsage
public int getRilDataRadioTechnology() {
- NetworkRegistrationInfo wwanRegInfo = getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- NetworkRegistrationInfo wlanRegInfo = getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
- if (wlanRegInfo != null
- && wlanRegInfo.getAccessNetworkTechnology() == TelephonyManager.NETWORK_TYPE_IWLAN
- && wlanRegInfo.getRegistrationState()
- == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
- return RIL_RADIO_TECHNOLOGY_IWLAN;
- } else if (wwanRegInfo != null) {
- return networkTypeToRilRadioTechnology(wwanRegInfo.getAccessNetworkTechnology());
- }
- return RIL_RADIO_TECHNOLOGY_UNKNOWN;
+ return networkTypeToRilRadioTechnology(getDataNetworkType());
}
+
/**
* @hide
* @Deprecated to be removed Q3 2013 use {@link #getRilDataRadioTechnology} or
@@ -1608,26 +1605,40 @@
}
}
- /** @hide */
+ /**
+ * Get current data network type.
+ *
+ * Note that for IWLAN AP-assisted mode device, which is reporting both camped access networks
+ * (cellular RAT and IWLAN)at the same time, this API is simulating the old legacy mode device
+ * behavior,
+ *
+ * @return Current data network type
+ * @hide
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public @TelephonyManager.NetworkType int getDataNetworkType() {
- final NetworkRegistrationInfo iwlanRegState = getNetworkRegistrationInfo(
+ final NetworkRegistrationInfo iwlanRegInfo = getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
- if (iwlanRegState != null && iwlanRegState.getRegistrationState()
- == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
- // If the device is on IWLAN, return IWLAN as the network type. This is to simulate the
- // behavior of legacy mode device. In the future caller should use
- // requestNetworkRegistrationInfo() to retrieve the actual data network type on cellular
- // or on IWLAN.
- return iwlanRegState.getAccessNetworkTechnology();
+ final NetworkRegistrationInfo wwanRegInfo = getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ // For legacy mode device, or AP-assisted mode device but IWLAN is out of service, use
+ // the RAT from cellular.
+ if (iwlanRegInfo == null || !iwlanRegInfo.isInService()) {
+ return (wwanRegInfo != null) ? wwanRegInfo.getAccessNetworkTechnology()
+ : TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
- final NetworkRegistrationInfo regState = getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- if (regState != null) {
- return regState.getAccessNetworkTechnology();
+ // At this point, it must be an AP-assisted mode device and IWLAN is in service. We should
+ // use the RAT from IWLAN service is cellular is out of service, or when both are in service
+ // and any APN type of data is preferred on IWLAN.
+ if (!wwanRegInfo.isInService() || mIsIwlanPreferred) {
+ return iwlanRegInfo.getAccessNetworkTechnology();
}
- return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
+ // If both cellular and IWLAN are in service, but no APN is preferred on IWLAN, still use
+ // the RAT from cellular.
+ return wwanRegInfo.getAccessNetworkTechnology();
}
/** @hide */
@@ -1976,4 +1987,14 @@
public String getOperatorAlphaShortRaw() {
return mOperatorAlphaShortRaw;
}
+
+ /**
+ * Set to {@code true} if any data network is preferred on IWLAN.
+ *
+ * @param isIwlanPreferred {@code true} if IWLAN is preferred.
+ * @hide
+ */
+ public void setIwlanPreferred(boolean isIwlanPreferred) {
+ mIsIwlanPreferred = isIwlanPreferred;
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d63c37a..2ba34c6 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5356,18 +5356,30 @@
* Returns the MMS user agent.
*/
public String getMmsUserAgent() {
- if (mContext == null) return null;
- return SubscriptionManager.getResourcesForSubId(mContext, getSubId()).getString(
- com.android.internal.R.string.config_mms_user_agent);
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getMmsUserAgent(getSubId());
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return null;
}
/**
* Returns the MMS user agent profile URL.
*/
public String getMmsUAProfUrl() {
- if (mContext == null) return null;
- return SubscriptionManager.getResourcesForSubId(mContext, getSubId()).getString(
- com.android.internal.R.string.config_mms_user_agent_profile_url);
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getMmsUAProfUrl(getSubId());
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return null;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 63aded1..cf1323a 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1986,4 +1986,14 @@
* outgoing SmsManager operation.
*/
oneway void enqueueSmsPickResult(String callingPackage, IIntegerConsumer subIdResult);
+
+ /**
+ * Returns the MMS user agent.
+ */
+ String getMmsUserAgent(int subId);
+
+ /**
+ * Returns the MMS user agent profile URL.
+ */
+ String getMmsUAProfUrl(int subId);
}
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index b5b0384..c16a0f4 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -569,7 +569,7 @@
.addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
- assertTrue(delta.toString(), delta.migrateTun(tunUid, tunIface, underlyingIface));
+ delta.migrateTun(tunUid, tunIface, new String[] {underlyingIface});
assertEquals(20, delta.size());
// tunIface and TEST_IFACE entries are not changed.
@@ -650,7 +650,7 @@
.addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L);
- assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
+ delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
assertEquals(9, delta.size());
// tunIface entries should not be changed.
@@ -813,6 +813,37 @@
}
@Test
+ public void testFilterDebugEntries() {
+ NetworkStats.Entry entry1 = new NetworkStats.Entry(
+ "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+ NetworkStats.Entry entry2 = new NetworkStats.Entry(
+ "test2", 10101, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+ NetworkStats.Entry entry3 = new NetworkStats.Entry(
+ "test2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+ NetworkStats.Entry entry4 = new NetworkStats.Entry(
+ "test2", 10101, SET_DBG_VPN_OUT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+ NetworkStats stats = new NetworkStats(TEST_START, 4)
+ .addValues(entry1)
+ .addValues(entry2)
+ .addValues(entry3)
+ .addValues(entry4);
+
+ stats.filterDebugEntries();
+
+ assertEquals(2, stats.size());
+ assertEquals(entry1, stats.getValues(0, null));
+ assertEquals(entry3, stats.getValues(1, null));
+ }
+
+ @Test
public void testApply464xlatAdjustments() {
final String v4Iface = "v4-wlan0";
final String baseIface = "wlan0";
diff --git a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
index 6e725dd..858358c 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
@@ -161,7 +161,7 @@
}
private void setHasCarrierPrivileges(boolean hasPrivileges) {
- when(mTm.checkCarrierPrivilegesForPackage(TEST_PKG)).thenReturn(
+ when(mTm.checkCarrierPrivilegesForPackageAnyPhone(TEST_PKG)).thenReturn(
hasPrivileges ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
: TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index bce526d..d9f2c20 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -57,11 +57,11 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -216,11 +216,16 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectSystemReady();
+ assertNull(mService.getTunAdjustedStats());
mService.systemReady();
+ // Verify that system ready fetches realtime stats and initializes tun adjusted stats.
+ verify(mNetManager).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL);
+ assertNotNull("failed to initialize TUN adjusted stats", mService.getTunAdjustedStats());
+ assertEquals(0, mService.getTunAdjustedStats().size());
+
mSession = mService.openSession();
assertNotNull("openSession() failed", mSession);
-
// catch INetworkManagementEventObserver during systemReady()
ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
@@ -733,11 +738,13 @@
NetworkStats stats = mService.getDetailedUidStats(ifaceFilter);
- verify(mNetManager, times(1)).getNetworkStatsUidDetail(eq(UID_ALL), argThat(ifaces ->
- ifaces != null && ifaces.length == 2
- && ArrayUtils.contains(ifaces, TEST_IFACE)
- && ArrayUtils.contains(ifaces, stackedIface)));
-
+ // mNetManager#getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL) has following invocations:
+ // 1) NetworkStatsService#systemReady from #setUp.
+ // 2) mService#forceUpdateIfaces in the test above.
+ // 3) Finally, mService#getDetailedUidStats.
+ verify(mNetManager, times(3)).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL);
+ assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), TEST_IFACE));
+ assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), stackedIface));
assertEquals(2, stats.size());
assertEquals(uidStats, stats.getValues(0, null));
assertEquals(tetheredStats1, stats.getValues(1, null));
@@ -923,11 +930,11 @@
}
@Test
- public void vpnWithOneUnderlyingIface() throws Exception {
+ public void vpnRewriteTrafficThroughItself() throws Exception {
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
expectDefaultSettings();
NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()};
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)};
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
@@ -938,23 +945,133 @@
getActiveIface(networkStates));
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
- // 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
- // 500 bytes (50 packets) were sent/received by UID_BLUE over VPN.
- // VPN sent/received 1650 bytes (150 packets) over WiFi.
- // Of 1650 bytes over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes attributed to
- // UID_BLUE, and 150 bytes attributed to UID_VPN for both rx/tx traffic.
+ //
+ // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
+ // over VPN.
+ // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
+ // over VPN.
+ //
+ // VPN UID rewrites packets read from TUN back to TUN, plus some of its own traffic
+ // (100 bytes).
incrementCurrentTime(HOUR_IN_MILLIS);
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L)
- .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 500L, 50L, 500L, 50L, 1L)
- .addValues(
- TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1650L, 150L, 1650L, 150L, 2L));
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 5)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 1000L, 100L, 1L)
+ .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 500L, 50L, 1L)
+ // VPN rewrites all the packets read from TUN + 100 additional bytes of VPN's
+ // own traffic.
+ .addValues(TUN_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 0L, 0L, 1600L, 160L, 2L)
+ // VPN sent 1760 bytes over WiFi in foreground (SET_FOREGROUND) i.e. 1600
+ // bytes (160 packets) + 1 byte/packet overhead (=160 bytes).
+ .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 1760L, 176L, 1L)
+ // VPN received 3300 bytes over WiFi in background (SET_DEFAULT) i.e. 3000 bytes
+ // (300 packets) + 1 byte/packet encryption overhead (=300 bytes).
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 3300L, 300L, 0L, 0L, 1L));
forcePollAndWaitForIdle();
- assertUidTotal(sTemplateWifi, UID_RED, 1000L, 100L, 1000L, 100L, 1);
- assertUidTotal(sTemplateWifi, UID_BLUE, 500L, 50L, 500L, 50L, 1);
- assertUidTotal(sTemplateWifi, UID_VPN, 150L, 0L, 150L, 0L, 2);
+ // Verify increased TUN usage by UID_VPN does not get attributed to other apps.
+ NetworkStats tunStats =
+ mService.getDetailedUidStats(new String[] {TUN_IFACE});
+ assertValues(
+ tunStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 2000L, 200L, 1000L, 100L, 1);
+ assertValues(
+ tunStats, TUN_IFACE, UID_BLUE, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 1000L, 100L, 500L, 50L, 1);
+ assertValues(
+ tunStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 0L, 0L, 1600L, 160L, 2);
+
+ // Verify correct attribution over WiFi.
+ assertUidTotal(sTemplateWifi, UID_RED, 2000L, 200L, 1000L, 100L, 1);
+ assertUidTotal(sTemplateWifi, UID_BLUE, 1000L, 100L, 500L, 50L, 1);
+ assertUidTotal(sTemplateWifi, UID_VPN, 300L, 0L, 260L, 26L, 2);
+ }
+
+ @Test
+ public void vpnWithOneUnderlyingIface() throws Exception {
+ // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
+ expectDefaultSettings();
+ NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()};
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectBandwidthControlCheck();
+
+ mService.forceUpdateIfaces(
+ new Network[] {WIFI_NETWORK, VPN_NETWORK},
+ vpnInfos,
+ networkStates,
+ getActiveIface(networkStates));
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
+ // over VPN.
+ // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
+ // over VPN.
+ // VPN sent 1650 bytes (150 packets), and received 3300 (300 packets) over WiFi.
+ // Of 1650 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes
+ // attributed to UID_BLUE, and 150 bytes attributed to UID_VPN.
+ // Of 3300 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes
+ // attributed to UID_BLUE, and 300 bytes attributed to UID_VPN.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 1000L, 100L, 1L)
+ .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 500L, 50L, 1L)
+ // VPN received 3300 bytes over WiFi in background (SET_DEFAULT).
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 3300L, 300L, 0L, 0L, 1L)
+ // VPN sent 1650 bytes over WiFi in foreground (SET_FOREGROUND).
+ .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 1650L, 150L, 1L));
+
+ forcePollAndWaitForIdle();
+
+ assertUidTotal(sTemplateWifi, UID_RED, 2000L, 200L, 1000L, 100L, 1);
+ assertUidTotal(sTemplateWifi, UID_BLUE, 1000L, 100L, 500L, 50L, 1);
+ assertUidTotal(sTemplateWifi, UID_VPN, 300L, 0L, 150L, 0L, 2);
+ }
+
+ @Test
+ public void vpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception {
+ // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
+ expectDefaultSettings();
+ NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()};
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectBandwidthControlCheck();
+
+ mService.forceUpdateIfaces(
+ new Network[] {WIFI_NETWORK, VPN_NETWORK},
+ vpnInfos,
+ networkStates,
+ getActiveIface(networkStates));
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
+ // over VPN.
+ // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
+ // over VPN.
+ // Additionally, the VPN sends 6000 bytes (600 packets) of its own traffic into the tun
+ // interface (passing that traffic to the VPN endpoint), and receives 5000 bytes (500
+ // packets) from it. Including overhead that is 6600/5500 bytes.
+ // VPN sent 8250 bytes (750 packets), and received 8800 (800 packets) over WiFi.
+ // Of 8250 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes
+ // attributed to UID_BLUE, and 6750 bytes attributed to UID_VPN.
+ // Of 8800 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes
+ // attributed to UID_BLUE, and 5800 bytes attributed to UID_VPN.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 1000L, 100L, 1L)
+ .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 500L, 50L, 1L)
+ .addValues(TUN_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 5000L, 500L, 6000L, 600L, 1L)
+ // VPN received 8800 bytes over WiFi in background (SET_DEFAULT).
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 8800L, 800L, 0L, 0L, 1L)
+ // VPN sent 8250 bytes over WiFi in foreground (SET_FOREGROUND).
+ .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 8250L, 750L, 1L));
+
+ forcePollAndWaitForIdle();
+
+ assertUidTotal(sTemplateWifi, UID_RED, 2000L, 200L, 1000L, 100L, 1);
+ assertUidTotal(sTemplateWifi, UID_BLUE, 1000L, 100L, 500L, 50L, 1);
+ assertUidTotal(sTemplateWifi, UID_VPN, 5800L, 500L, 6750L, 600L, 2);
}
@Test
@@ -962,7 +1079,7 @@
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
expectDefaultSettings();
NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()};
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)};
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
@@ -993,6 +1110,136 @@
}
@Test
+ public void vpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception {
+ // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
+ // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
+ // Additionally, VPN is duplicating traffic across both WiFi and Cell.
+ expectDefaultSettings();
+ NetworkState[] networkStates =
+ new NetworkState[] {
+ buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()
+ };
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectBandwidthControlCheck();
+
+ mService.forceUpdateIfaces(
+ new Network[] {WIFI_NETWORK, VPN_NETWORK},
+ vpnInfos,
+ networkStates,
+ getActiveIface(networkStates));
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent/received by UID_RED and UID_BLUE over VPN.
+ // VPN sent/received 4400 bytes (400 packets) over both WiFi and Cell (8800 bytes in total).
+ // Of 8800 bytes over WiFi/Cell, expect:
+ // - 500 bytes rx/tx each over WiFi/Cell attributed to both UID_RED and UID_BLUE.
+ // - 1200 bytes rx/tx each over WiFi/Cell for VPN_UID.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L)
+ .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 2200L, 200L, 2L)
+ .addValues(
+ TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 2200L, 200L, 2L));
+
+ forcePollAndWaitForIdle();
+
+ assertUidTotal(sTemplateWifi, UID_RED, 500L, 50L, 500L, 50L, 1);
+ assertUidTotal(sTemplateWifi, UID_BLUE, 500L, 50L, 500L, 50L, 1);
+ assertUidTotal(sTemplateWifi, UID_VPN, 1200L, 100L, 1200L, 100L, 2);
+
+ assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 500L, 50L, 500L, 50L, 1);
+ assertUidTotal(buildTemplateMobileWildcard(), UID_BLUE, 500L, 50L, 500L, 50L, 1);
+ assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 1200L, 100L, 1200L, 100L, 2);
+ }
+
+ @Test
+ public void vpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception {
+ // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
+ // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
+ // Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell.
+ expectDefaultSettings();
+ NetworkState[] networkStates =
+ new NetworkState[] {
+ buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()
+ };
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectBandwidthControlCheck();
+
+ mService.forceUpdateIfaces(
+ new Network[] {WIFI_NETWORK, VPN_NETWORK},
+ vpnInfos,
+ networkStates,
+ getActiveIface(networkStates));
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent, and 500 bytes (50 packets) received by UID_RED over
+ // VPN.
+ // VPN sent 660 bytes (60 packets) over WiFi and 440 bytes (40 packets) over Cell.
+ // And, it received 330 bytes (30 packets) over WiFi and 220 bytes (20 packets) over Cell.
+ // For UID_RED, expect 600 bytes attributed over WiFi and 400 bytes over Cell for sent (tx)
+ // traffic. For received (rx) traffic, expect 300 bytes over WiFi and 200 bytes over Cell.
+ //
+ // For UID_VPN, expect 60 bytes attributed over WiFi and 40 bytes over Cell for tx traffic.
+ // And, 30 bytes over WiFi and 20 bytes over Cell for rx traffic.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 500L, 50L, 1000L, 100L, 2L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 330L, 30L, 660L, 60L, 1L)
+ .addValues(TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 220L, 20L, 440L, 40L, 1L));
+
+ forcePollAndWaitForIdle();
+
+ assertUidTotal(sTemplateWifi, UID_RED, 300L, 30L, 600L, 60L, 1);
+ assertUidTotal(sTemplateWifi, UID_VPN, 30L, 0L, 60L, 0L, 1);
+
+ assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 200L, 20L, 400L, 40L, 1);
+ assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 20L, 0L, 40L, 0L, 1);
+ }
+
+ @Test
+ public void vpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception {
+ // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
+ // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
+ // Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell.
+ expectDefaultSettings();
+ NetworkState[] networkStates =
+ new NetworkState[] {
+ buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()
+ };
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectBandwidthControlCheck();
+
+ mService.forceUpdateIfaces(
+ new Network[] {WIFI_NETWORK, VPN_NETWORK},
+ vpnInfos,
+ networkStates,
+ getActiveIface(networkStates));
+ // create some traffic (assume 10 bytes of MTU for VPN interface:
+ // 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
+ // VPN sent/received 600 bytes (60 packets) over WiFi and 200 bytes (20 packets) over Cell.
+ // For UID_RED, expect 600 bytes attributed over WiFi and 200 bytes over Cell for both
+ // rx/tx.
+ // UID_VPN gets nothing attributed to it (avoiding negative stats).
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 600L, 60L, 600L, 60L, 0L)
+ .addValues(TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 200L, 20L, 200L, 20L, 0L));
+
+ forcePollAndWaitForIdle();
+
+ assertUidTotal(sTemplateWifi, UID_RED, 600L, 60L, 600L, 60L, 0);
+ assertUidTotal(sTemplateWifi, UID_VPN, 0L, 0L, 0L, 0L, 0);
+
+ assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 200L, 20L, 200L, 20L, 0);
+ assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 0L, 0L, 0L, 0L, 0);
+ }
+
+ @Test
public void vpnWithIncorrectUnderlyingIface() throws Exception {
// WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2),
// but has declared only WiFi (TEST_IFACE) in its underlying network set.
@@ -1001,7 +1248,7 @@
new NetworkState[] {
buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()
};
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)};
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
@@ -1030,6 +1277,134 @@
}
@Test
+ public void recordSnapshot_migratesTunTrafficAndUpdatesTunAdjustedStats() throws Exception {
+ assertEquals(0, mService.getTunAdjustedStats().size());
+ // VPN using WiFi (TEST_IFACE).
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ expectBandwidthControlCheck();
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were downloaded by UID_RED over VPN.
+ // VPN received 1100 bytes (100 packets) over WiFi.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L));
+
+ // this should lead to NSS#recordSnapshotLocked
+ mService.forceUpdateIfaces(
+ new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */);
+
+ // Verify TUN adjusted stats have traffic migrated correctly.
+ // Of 1100 bytes VPN received over WiFi, expect 1000 bytes attributed to UID_RED and 100
+ // bytes attributed to UID_VPN.
+ NetworkStats tunAdjStats = mService.getTunAdjustedStats();
+ assertValues(
+ tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0);
+ assertValues(
+ tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0);
+ }
+
+ @Test
+ public void getDetailedUidStats_migratesTunTrafficAndUpdatesTunAdjustedStats()
+ throws Exception {
+ assertEquals(0, mService.getTunAdjustedStats().size());
+ // VPN using WiFi (TEST_IFACE).
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ expectBandwidthControlCheck();
+ mService.forceUpdateIfaces(
+ new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */);
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were downloaded by UID_RED over VPN.
+ // VPN received 1100 bytes (100 packets) over WiFi.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L));
+
+ mService.getDetailedUidStats(INTERFACES_ALL);
+
+ // Verify internally maintained TUN adjusted stats
+ NetworkStats tunAdjStats = mService.getTunAdjustedStats();
+ // Verify stats for TEST_IFACE (WiFi):
+ // Of 1100 bytes VPN received over WiFi, expect 1000 bytes attributed to UID_RED and 100
+ // bytes attributed to UID_VPN.
+ assertValues(
+ tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0);
+ assertValues(
+ tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0);
+ // Verify stats for TUN_IFACE; only UID_RED should have usage on it.
+ assertValues(
+ tunAdjStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0);
+ assertValues(
+ tunAdjStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 0);
+
+ // lets assume that since last time, VPN received another 1100 bytes (same assumptions as
+ // before i.e. UID_RED downloaded another 1000 bytes).
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ // Note - NetworkStatsFactory returns counters that are monotonically increasing.
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 0L, 0L, 0L));
+
+ mService.getDetailedUidStats(INTERFACES_ALL);
+
+ tunAdjStats = mService.getTunAdjustedStats();
+ // verify TEST_IFACE stats:
+ assertValues(
+ tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 2000L, 200L, 0L, 0L, 0);
+ assertValues(
+ tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 200L, 0L, 0L, 0L, 0);
+ // verify TUN_IFACE stats:
+ assertValues(
+ tunAdjStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 2000L, 200L, 0L, 0L, 0);
+ assertValues(
+ tunAdjStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 0);
+ }
+
+ @Test
+ public void getDetailedUidStats_returnsCorrectStatsWithVpnRunning() throws Exception {
+ // VPN using WiFi (TEST_IFACE).
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ expectBandwidthControlCheck();
+ mService.forceUpdateIfaces(
+ new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */);
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were downloaded by UID_RED over VPN.
+ // VPN received 1100 bytes (100 packets) over WiFi.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L));
+
+ // Query realtime stats for TEST_IFACE.
+ NetworkStats queriedStats =
+ mService.getDetailedUidStats(new String[] {TEST_IFACE});
+
+ assertEquals(HOUR_IN_MILLIS, queriedStats.getElapsedRealtime());
+ // verify that returned stats are only for TEST_IFACE and VPN traffic is migrated correctly.
+ assertEquals(new String[] {TEST_IFACE}, queriedStats.getUniqueIfaces());
+ assertValues(
+ queriedStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0);
+ assertValues(
+ queriedStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0);
+ }
+
+ @Test
public void testRegisterUsageCallback() throws Exception {
// pretend that wifi network comes online; service should ask about full
// network state, and poll any existing interfaces before updating.
@@ -1382,11 +1757,11 @@
return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);
}
- private static VpnInfo createVpnInfo(String underlyingIface) {
+ private static VpnInfo createVpnInfo(String[] underlyingIfaces) {
VpnInfo info = new VpnInfo();
info.ownerUid = UID_VPN;
info.vpnIface = TUN_IFACE;
- info.primaryUnderlyingIface = underlyingIface;
+ info.underlyingIfaces = underlyingIfaces;
return info;
}