diff options
115 files changed, 6012 insertions, 1682 deletions
diff --git a/Android.mk b/Android.mk index 69e57b6672f2..86f133ce22f6 100644 --- a/Android.mk +++ b/Android.mk @@ -125,6 +125,7 @@ LOCAL_SRC_FILES += \ core/java/android/bluetooth/IBluetoothGatt.aidl \ core/java/android/bluetooth/IBluetoothGattCallback.aidl \ core/java/android/bluetooth/IBluetoothGattServerCallback.aidl \ + core/java/android/bluetooth/le/IAdvertiserCallback.aidl \ core/java/android/content/IClipboard.aidl \ core/java/android/content/IContentService.aidl \ core/java/android/content/IIntentReceiver.aidl \ @@ -451,7 +452,7 @@ LOCAL_SRC_FILES += \ wifi/java/android/net/wifi/IWifiManager.aidl \ wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl \ wifi/java/android/net/wifi/nan/IWifiNanManager.aidl \ - wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl \ + wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl \ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \ wifi/java/android/net/wifi/IWifiScanner.aidl \ wifi/java/android/net/wifi/IRttManager.aidl \ @@ -484,6 +485,12 @@ LOCAL_INTERMEDIATE_SOURCES := \ $(framework_res_source_path)/android/Manifest.java \ $(framework_res_source_path)/com/android/internal/R.java +# Make sure that R.java and Manifest.java are built before we build +# the source for this library. +framework_res_R_stamp := \ + $(call intermediates-dir-for,APPS,framework-res,,COMMON)/src/R.stamp +LOCAL_ADDITIONAL_DEPENDENCIES := $(framework_res_R_stamp) + LOCAL_NO_STANDARD_LIBRARIES := true LOCAL_JAVA_LIBRARIES := core-oj core-libart core-lambda-stubs conscrypt okhttp core-junit bouncycastle ext LOCAL_STATIC_JAVA_LIBRARIES := framework-protos @@ -495,15 +502,8 @@ LOCAL_JACK_FLAGS := --multi-dex native LOCAL_RMTYPEDEFS := true include $(BUILD_JAVA_LIBRARY) -framework_module := $(LOCAL_INSTALLED_MODULE) - -# Make sure that R.java and Manifest.java are built before we build -# the source for this library. -framework_res_R_stamp := \ - $(call intermediates-dir-for,APPS,framework-res,,COMMON)/src/R.stamp -$(full_classes_compiled_jar): $(framework_res_R_stamp) -$(built_dex_intermediate): $(framework_res_R_stamp) +framework_module := $(LOCAL_INSTALLED_MODULE) $(framework_module): | $(dir $(framework_module))framework-res.apk framework_built := $(call java-lib-deps,framework) diff --git a/api/current.txt b/api/current.txt index 5a7c100b6871..8a38b2136bca 100644 --- a/api/current.txt +++ b/api/current.txt @@ -35314,6 +35314,7 @@ package android.system { method public static java.net.SocketAddress getsockname(java.io.FileDescriptor) throws android.system.ErrnoException; method public static int gettid(); method public static int getuid(); + method public static byte[] getxattr(java.lang.String, java.lang.String) throws android.system.ErrnoException; method public static java.lang.String if_indextoname(int); method public static int if_nametoindex(java.lang.String); method public static java.net.InetAddress inet_pton(int, java.lang.String); @@ -35322,6 +35323,7 @@ package android.system { method public static void lchown(java.lang.String, int, int) throws android.system.ErrnoException; method public static void link(java.lang.String, java.lang.String) throws android.system.ErrnoException; method public static void listen(java.io.FileDescriptor, int) throws android.system.ErrnoException; + method public static java.lang.String[] listxattr(java.lang.String) throws android.system.ErrnoException; method public static long lseek(java.io.FileDescriptor, long, int) throws android.system.ErrnoException; method public static android.system.StructStat lstat(java.lang.String) throws android.system.ErrnoException; method public static void mincore(long, long, byte[]) throws android.system.ErrnoException; @@ -35348,6 +35350,7 @@ package android.system { method public static int recvfrom(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static int recvfrom(java.io.FileDescriptor, byte[], int, int, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static void remove(java.lang.String) throws android.system.ErrnoException; + method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException; method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException; method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException; method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; @@ -35359,6 +35362,7 @@ package android.system { method public static int setsid() throws android.system.ErrnoException; method public static void setsockoptInt(java.io.FileDescriptor, int, int, int) throws android.system.ErrnoException; method public static void setuid(int) throws android.system.ErrnoException; + method public static void setxattr(java.lang.String, java.lang.String, byte[], int) throws android.system.ErrnoException; method public static void shutdown(java.io.FileDescriptor, int) throws android.system.ErrnoException; method public static java.io.FileDescriptor socket(int, int, int) throws android.system.ErrnoException; method public static void socketpair(int, int, int, java.io.FileDescriptor, java.io.FileDescriptor) throws android.system.ErrnoException; @@ -50501,6 +50505,7 @@ package java.lang { method public boolean isPrimitive(); method public boolean isSynthetic(); method public T newInstance() throws java.lang.IllegalAccessException, java.lang.InstantiationException; + method public java.lang.String toGenericString(); } public class ClassCastException extends java.lang.RuntimeException { @@ -51891,7 +51896,9 @@ package java.lang.reflect { method public abstract java.lang.annotation.Annotation[][] getParameterAnnotations(); method public int getParameterCount(); method public abstract java.lang.Class<?>[] getParameterTypes(); + method public java.lang.reflect.Parameter[] getParameters(); method public abstract java.lang.reflect.TypeVariable<?>[] getTypeParameters(); + method public final boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>); method public boolean isSynthetic(); method public boolean isVarArgs(); method public abstract java.lang.String toGenericString(); @@ -51998,6 +52005,7 @@ package java.lang.reflect { method public static boolean isTransient(int); method public static boolean isVolatile(int); method public static int methodModifiers(); + method public static int parameterModifiers(); method public static java.lang.String toString(int); field public static final int ABSTRACT = 1024; // 0x400 field public static final int FINAL = 16; // 0x10 @@ -52013,6 +52021,21 @@ package java.lang.reflect { field public static final int VOLATILE = 64; // 0x40 } + public final class Parameter implements java.lang.reflect.AnnotatedElement { + method public T getAnnotation(java.lang.Class<T>); + method public java.lang.annotation.Annotation[] getAnnotations(); + method public java.lang.annotation.Annotation[] getDeclaredAnnotations(); + method public java.lang.reflect.Executable getDeclaringExecutable(); + method public int getModifiers(); + method public java.lang.String getName(); + method public java.lang.reflect.Type getParameterizedType(); + method public java.lang.Class<?> getType(); + method public boolean isImplicit(); + method public boolean isNamePresent(); + method public boolean isSynthetic(); + method public boolean isVarArgs(); + } + public abstract interface ParameterizedType implements java.lang.reflect.Type { method public abstract java.lang.reflect.Type[] getActualTypeArguments(); method public abstract java.lang.reflect.Type getOwnerType(); @@ -54775,6 +54798,13 @@ package java.security { method public abstract java.security.ProtectionDomain[] combine(java.security.ProtectionDomain[], java.security.ProtectionDomain[]); } + public final class DomainLoadStoreParameter implements java.security.KeyStore.LoadStoreParameter { + ctor public DomainLoadStoreParameter(java.net.URI, java.util.Map<java.lang.String, java.security.KeyStore.ProtectionParameter>); + method public java.net.URI getConfiguration(); + method public java.security.KeyStore.ProtectionParameter getProtectionParameter(); + method public java.util.Map<java.lang.String, java.security.KeyStore.ProtectionParameter> getProtectionParams(); + } + public class GeneralSecurityException extends java.lang.Exception { ctor public GeneralSecurityException(); ctor public GeneralSecurityException(java.lang.String); @@ -54971,6 +55001,12 @@ package java.security { } public static abstract interface KeyStore.Entry { + method public default java.util.Set<java.security.KeyStore.Entry.Attribute> getAttributes(); + } + + public static abstract interface KeyStore.Entry.Attribute { + method public abstract java.lang.String getName(); + method public abstract java.lang.String getValue(); } public static abstract interface KeyStore.LoadStoreParameter { @@ -54979,11 +55015,15 @@ package java.security { public static class KeyStore.PasswordProtection implements javax.security.auth.Destroyable java.security.KeyStore.ProtectionParameter { ctor public KeyStore.PasswordProtection(char[]); + ctor public KeyStore.PasswordProtection(char[], java.lang.String, java.security.spec.AlgorithmParameterSpec); method public synchronized char[] getPassword(); + method public java.lang.String getProtectionAlgorithm(); + method public java.security.spec.AlgorithmParameterSpec getProtectionParameters(); } public static final class KeyStore.PrivateKeyEntry implements java.security.KeyStore.Entry { ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[]); + ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[], java.util.Set<java.security.KeyStore.Entry.Attribute>); method public java.security.cert.Certificate getCertificate(); method public java.security.cert.Certificate[] getCertificateChain(); method public java.security.PrivateKey getPrivateKey(); @@ -54994,11 +55034,13 @@ package java.security { public static final class KeyStore.SecretKeyEntry implements java.security.KeyStore.Entry { ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey); + ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey, java.util.Set<java.security.KeyStore.Entry.Attribute>); method public javax.crypto.SecretKey getSecretKey(); } public static final class KeyStore.TrustedCertificateEntry implements java.security.KeyStore.Entry { ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate); + ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate, java.util.Set<java.security.KeyStore.Entry.Attribute>); method public java.security.cert.Certificate getTrustedCertificate(); } @@ -55077,6 +55119,14 @@ package java.security { ctor public NoSuchProviderException(java.lang.String); } + public final class PKCS12Attribute implements java.security.KeyStore.Entry.Attribute { + ctor public PKCS12Attribute(java.lang.String, java.lang.String); + ctor public PKCS12Attribute(byte[]); + method public byte[] getEncoded(); + method public java.lang.String getName(); + method public java.lang.String getValue(); + } + public abstract class Permission implements java.security.Guard java.io.Serializable { ctor public Permission(java.lang.String); method public void checkGuard(java.lang.Object) throws java.lang.SecurityException; @@ -55134,10 +55184,11 @@ package java.security { method public abstract boolean equals(java.lang.Object); method public abstract java.lang.String getName(); method public abstract int hashCode(); + method public default boolean implies(javax.security.auth.Subject); method public abstract java.lang.String toString(); } - public abstract interface PrivateKey implements java.security.Key { + public abstract interface PrivateKey implements javax.security.auth.Destroyable java.security.Key { field public static final long serialVersionUID = 6034044314589513430L; // 0x53bd3b559a12c6d6L } @@ -55166,16 +55217,25 @@ package java.security { public abstract class Provider extends java.util.Properties { ctor protected Provider(java.lang.String, double, java.lang.String); + method public synchronized java.lang.Object compute(java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>); + method public synchronized java.lang.Object computeIfAbsent(java.lang.Object, java.util.function.Function<? super java.lang.Object, ? extends java.lang.Object>); + method public synchronized java.lang.Object computeIfPresent(java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>); method public synchronized void forEach(java.util.function.BiConsumer<? super java.lang.Object, ? super java.lang.Object>); method public java.lang.String getInfo(); method public java.lang.String getName(); + method public synchronized java.lang.Object getOrDefault(java.lang.Object, java.lang.Object); method public synchronized java.security.Provider.Service getService(java.lang.String, java.lang.String); method public synchronized java.util.Set<java.security.Provider.Service> getServices(); method public double getVersion(); + method public synchronized java.lang.Object merge(java.lang.Object, java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>); method public synchronized java.lang.Object put(java.lang.Object, java.lang.Object); method public synchronized void putAll(java.util.Map<?, ?>); + method public synchronized java.lang.Object putIfAbsent(java.lang.Object, java.lang.Object); method protected synchronized void putService(java.security.Provider.Service); method protected synchronized void removeService(java.security.Provider.Service); + method public synchronized boolean replace(java.lang.Object, java.lang.Object, java.lang.Object); + method public synchronized java.lang.Object replace(java.lang.Object, java.lang.Object); + method public synchronized void replaceAll(java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>); } public static class Provider.Service { @@ -55217,6 +55277,7 @@ package java.security { method public static java.security.SecureRandom getInstance(java.lang.String) throws java.security.NoSuchAlgorithmException; method public static java.security.SecureRandom getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException; method public static java.security.SecureRandom getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException; + method public static java.security.SecureRandom getInstanceStrong() throws java.security.NoSuchAlgorithmException; method public final java.security.Provider getProvider(); method public static byte[] getSeed(int); method protected final int next(int); @@ -55835,6 +55896,7 @@ package java.security.cert { method public abstract int getVersion(); method public abstract void verify(java.security.PublicKey) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException; method public abstract void verify(java.security.PublicKey, java.lang.String) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException; + method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException; } public abstract class X509CRLEntry implements java.security.cert.X509Extension { @@ -58282,6 +58344,33 @@ package java.util { method public static java.lang.String toString(java.lang.Object[]); } + public class Base64 { + method public static java.util.Base64.Decoder getDecoder(); + method public static java.util.Base64.Encoder getEncoder(); + method public static java.util.Base64.Decoder getMimeDecoder(); + method public static java.util.Base64.Encoder getMimeEncoder(); + method public static java.util.Base64.Encoder getMimeEncoder(int, byte[]); + method public static java.util.Base64.Decoder getUrlDecoder(); + method public static java.util.Base64.Encoder getUrlEncoder(); + } + + public static class Base64.Decoder { + method public byte[] decode(byte[]); + method public byte[] decode(java.lang.String); + method public int decode(byte[], byte[]); + method public java.nio.ByteBuffer decode(java.nio.ByteBuffer); + method public java.io.InputStream wrap(java.io.InputStream); + } + + public static class Base64.Encoder { + method public byte[] encode(byte[]); + method public int encode(byte[], byte[]); + method public java.nio.ByteBuffer encode(java.nio.ByteBuffer); + method public java.lang.String encodeToString(byte[]); + method public java.util.Base64.Encoder withoutPadding(); + method public java.io.OutputStream wrap(java.io.OutputStream); + } + public class BitSet implements java.lang.Cloneable java.io.Serializable { ctor public BitSet(); ctor public BitSet(int); diff --git a/api/system-current.txt b/api/system-current.txt index f57851c913b4..175ec87ed399 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -38130,6 +38130,7 @@ package android.system { method public static java.net.SocketAddress getsockname(java.io.FileDescriptor) throws android.system.ErrnoException; method public static int gettid(); method public static int getuid(); + method public static byte[] getxattr(java.lang.String, java.lang.String) throws android.system.ErrnoException; method public static java.lang.String if_indextoname(int); method public static int if_nametoindex(java.lang.String); method public static java.net.InetAddress inet_pton(int, java.lang.String); @@ -38138,6 +38139,7 @@ package android.system { method public static void lchown(java.lang.String, int, int) throws android.system.ErrnoException; method public static void link(java.lang.String, java.lang.String) throws android.system.ErrnoException; method public static void listen(java.io.FileDescriptor, int) throws android.system.ErrnoException; + method public static java.lang.String[] listxattr(java.lang.String) throws android.system.ErrnoException; method public static long lseek(java.io.FileDescriptor, long, int) throws android.system.ErrnoException; method public static android.system.StructStat lstat(java.lang.String) throws android.system.ErrnoException; method public static void mincore(long, long, byte[]) throws android.system.ErrnoException; @@ -38164,6 +38166,7 @@ package android.system { method public static int recvfrom(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static int recvfrom(java.io.FileDescriptor, byte[], int, int, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static void remove(java.lang.String) throws android.system.ErrnoException; + method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException; method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException; method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException; method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; @@ -38175,6 +38178,7 @@ package android.system { method public static int setsid() throws android.system.ErrnoException; method public static void setsockoptInt(java.io.FileDescriptor, int, int, int) throws android.system.ErrnoException; method public static void setuid(int) throws android.system.ErrnoException; + method public static void setxattr(java.lang.String, java.lang.String, byte[], int) throws android.system.ErrnoException; method public static void shutdown(java.io.FileDescriptor, int) throws android.system.ErrnoException; method public static java.io.FileDescriptor socket(int, int, int) throws android.system.ErrnoException; method public static void socketpair(int, int, int, java.io.FileDescriptor, java.io.FileDescriptor) throws android.system.ErrnoException; @@ -53857,6 +53861,7 @@ package java.lang { method public boolean isPrimitive(); method public boolean isSynthetic(); method public T newInstance() throws java.lang.IllegalAccessException, java.lang.InstantiationException; + method public java.lang.String toGenericString(); } public class ClassCastException extends java.lang.RuntimeException { @@ -55247,7 +55252,9 @@ package java.lang.reflect { method public abstract java.lang.annotation.Annotation[][] getParameterAnnotations(); method public int getParameterCount(); method public abstract java.lang.Class<?>[] getParameterTypes(); + method public java.lang.reflect.Parameter[] getParameters(); method public abstract java.lang.reflect.TypeVariable<?>[] getTypeParameters(); + method public final boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>); method public boolean isSynthetic(); method public boolean isVarArgs(); method public abstract java.lang.String toGenericString(); @@ -55354,6 +55361,7 @@ package java.lang.reflect { method public static boolean isTransient(int); method public static boolean isVolatile(int); method public static int methodModifiers(); + method public static int parameterModifiers(); method public static java.lang.String toString(int); field public static final int ABSTRACT = 1024; // 0x400 field public static final int FINAL = 16; // 0x10 @@ -55369,6 +55377,21 @@ package java.lang.reflect { field public static final int VOLATILE = 64; // 0x40 } + public final class Parameter implements java.lang.reflect.AnnotatedElement { + method public T getAnnotation(java.lang.Class<T>); + method public java.lang.annotation.Annotation[] getAnnotations(); + method public java.lang.annotation.Annotation[] getDeclaredAnnotations(); + method public java.lang.reflect.Executable getDeclaringExecutable(); + method public int getModifiers(); + method public java.lang.String getName(); + method public java.lang.reflect.Type getParameterizedType(); + method public java.lang.Class<?> getType(); + method public boolean isImplicit(); + method public boolean isNamePresent(); + method public boolean isSynthetic(); + method public boolean isVarArgs(); + } + public abstract interface ParameterizedType implements java.lang.reflect.Type { method public abstract java.lang.reflect.Type[] getActualTypeArguments(); method public abstract java.lang.reflect.Type getOwnerType(); @@ -58131,6 +58154,13 @@ package java.security { method public abstract java.security.ProtectionDomain[] combine(java.security.ProtectionDomain[], java.security.ProtectionDomain[]); } + public final class DomainLoadStoreParameter implements java.security.KeyStore.LoadStoreParameter { + ctor public DomainLoadStoreParameter(java.net.URI, java.util.Map<java.lang.String, java.security.KeyStore.ProtectionParameter>); + method public java.net.URI getConfiguration(); + method public java.security.KeyStore.ProtectionParameter getProtectionParameter(); + method public java.util.Map<java.lang.String, java.security.KeyStore.ProtectionParameter> getProtectionParams(); + } + public class GeneralSecurityException extends java.lang.Exception { ctor public GeneralSecurityException(); ctor public GeneralSecurityException(java.lang.String); @@ -58327,6 +58357,12 @@ package java.security { } public static abstract interface KeyStore.Entry { + method public default java.util.Set<java.security.KeyStore.Entry.Attribute> getAttributes(); + } + + public static abstract interface KeyStore.Entry.Attribute { + method public abstract java.lang.String getName(); + method public abstract java.lang.String getValue(); } public static abstract interface KeyStore.LoadStoreParameter { @@ -58335,11 +58371,15 @@ package java.security { public static class KeyStore.PasswordProtection implements javax.security.auth.Destroyable java.security.KeyStore.ProtectionParameter { ctor public KeyStore.PasswordProtection(char[]); + ctor public KeyStore.PasswordProtection(char[], java.lang.String, java.security.spec.AlgorithmParameterSpec); method public synchronized char[] getPassword(); + method public java.lang.String getProtectionAlgorithm(); + method public java.security.spec.AlgorithmParameterSpec getProtectionParameters(); } public static final class KeyStore.PrivateKeyEntry implements java.security.KeyStore.Entry { ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[]); + ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[], java.util.Set<java.security.KeyStore.Entry.Attribute>); method public java.security.cert.Certificate getCertificate(); method public java.security.cert.Certificate[] getCertificateChain(); method public java.security.PrivateKey getPrivateKey(); @@ -58350,11 +58390,13 @@ package java.security { public static final class KeyStore.SecretKeyEntry implements java.security.KeyStore.Entry { ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey); + ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey, java.util.Set<java.security.KeyStore.Entry.Attribute>); method public javax.crypto.SecretKey getSecretKey(); } public static final class KeyStore.TrustedCertificateEntry implements java.security.KeyStore.Entry { ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate); + ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate, java.util.Set<java.security.KeyStore.Entry.Attribute>); method public java.security.cert.Certificate getTrustedCertificate(); } @@ -58433,6 +58475,14 @@ package java.security { ctor public NoSuchProviderException(java.lang.String); } + public final class PKCS12Attribute implements java.security.KeyStore.Entry.Attribute { + ctor public PKCS12Attribute(java.lang.String, java.lang.String); + ctor public PKCS12Attribute(byte[]); + method public byte[] getEncoded(); + method public java.lang.String getName(); + method public java.lang.String getValue(); + } + public abstract class Permission implements java.security.Guard java.io.Serializable { ctor public Permission(java.lang.String); method public void checkGuard(java.lang.Object) throws java.lang.SecurityException; @@ -58490,10 +58540,11 @@ package java.security { method public abstract boolean equals(java.lang.Object); method public abstract java.lang.String getName(); method public abstract int hashCode(); + method public default boolean implies(javax.security.auth.Subject); method public abstract java.lang.String toString(); } - public abstract interface PrivateKey implements java.security.Key { + public abstract interface PrivateKey implements javax.security.auth.Destroyable java.security.Key { field public static final long serialVersionUID = 6034044314589513430L; // 0x53bd3b559a12c6d6L } @@ -58522,16 +58573,25 @@ package java.security { public abstract class Provider extends java.util.Properties { ctor protected Provider(java.lang.String, double, java.lang.String); + method public synchronized java.lang.Object compute(java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>); + method public synchronized java.lang.Object computeIfAbsent(java.lang.Object, java.util.function.Function<? super java.lang.Object, ? extends java.lang.Object>); + method public synchronized java.lang.Object computeIfPresent(java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>); method public synchronized void forEach(java.util.function.BiConsumer<? super java.lang.Object, ? super java.lang.Object>); method public java.lang.String getInfo(); method public java.lang.String getName(); + method public synchronized java.lang.Object getOrDefault(java.lang.Object, java.lang.Object); method public synchronized java.security.Provider.Service getService(java.lang.String, java.lang.String); method public synchronized java.util.Set<java.security.Provider.Service> getServices(); method public double getVersion(); + method public synchronized java.lang.Object merge(java.lang.Object, java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>); method public synchronized java.lang.Object put(java.lang.Object, java.lang.Object); method public synchronized void putAll(java.util.Map<?, ?>); + method public synchronized java.lang.Object putIfAbsent(java.lang.Object, java.lang.Object); method protected synchronized void putService(java.security.Provider.Service); method protected synchronized void removeService(java.security.Provider.Service); + method public synchronized boolean replace(java.lang.Object, java.lang.Object, java.lang.Object); + method public synchronized java.lang.Object replace(java.lang.Object, java.lang.Object); + method public synchronized void replaceAll(java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>); } public static class Provider.Service { @@ -58573,6 +58633,7 @@ package java.security { method public static java.security.SecureRandom getInstance(java.lang.String) throws java.security.NoSuchAlgorithmException; method public static java.security.SecureRandom getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException; method public static java.security.SecureRandom getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException; + method public static java.security.SecureRandom getInstanceStrong() throws java.security.NoSuchAlgorithmException; method public final java.security.Provider getProvider(); method public static byte[] getSeed(int); method protected final int next(int); @@ -59191,6 +59252,7 @@ package java.security.cert { method public abstract int getVersion(); method public abstract void verify(java.security.PublicKey) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException; method public abstract void verify(java.security.PublicKey, java.lang.String) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException; + method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException; } public abstract class X509CRLEntry implements java.security.cert.X509Extension { @@ -61638,6 +61700,33 @@ package java.util { method public static java.lang.String toString(java.lang.Object[]); } + public class Base64 { + method public static java.util.Base64.Decoder getDecoder(); + method public static java.util.Base64.Encoder getEncoder(); + method public static java.util.Base64.Decoder getMimeDecoder(); + method public static java.util.Base64.Encoder getMimeEncoder(); + method public static java.util.Base64.Encoder getMimeEncoder(int, byte[]); + method public static java.util.Base64.Decoder getUrlDecoder(); + method public static java.util.Base64.Encoder getUrlEncoder(); + } + + public static class Base64.Decoder { + method public byte[] decode(byte[]); + method public byte[] decode(java.lang.String); + method public int decode(byte[], byte[]); + method public java.nio.ByteBuffer decode(java.nio.ByteBuffer); + method public java.io.InputStream wrap(java.io.InputStream); + } + + public static class Base64.Encoder { + method public byte[] encode(byte[]); + method public int encode(byte[], byte[]); + method public java.nio.ByteBuffer encode(java.nio.ByteBuffer); + method public java.lang.String encodeToString(byte[]); + method public java.util.Base64.Encoder withoutPadding(); + method public java.io.OutputStream wrap(java.io.OutputStream); + } + public class BitSet implements java.lang.Cloneable java.io.Serializable { ctor public BitSet(); ctor public BitSet(int); diff --git a/api/test-current.txt b/api/test-current.txt index 3ac97575d4a8..3544efbce3eb 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -35391,6 +35391,7 @@ package android.system { method public static java.net.SocketAddress getsockname(java.io.FileDescriptor) throws android.system.ErrnoException; method public static int gettid(); method public static int getuid(); + method public static byte[] getxattr(java.lang.String, java.lang.String) throws android.system.ErrnoException; method public static java.lang.String if_indextoname(int); method public static int if_nametoindex(java.lang.String); method public static java.net.InetAddress inet_pton(int, java.lang.String); @@ -35399,6 +35400,7 @@ package android.system { method public static void lchown(java.lang.String, int, int) throws android.system.ErrnoException; method public static void link(java.lang.String, java.lang.String) throws android.system.ErrnoException; method public static void listen(java.io.FileDescriptor, int) throws android.system.ErrnoException; + method public static java.lang.String[] listxattr(java.lang.String) throws android.system.ErrnoException; method public static long lseek(java.io.FileDescriptor, long, int) throws android.system.ErrnoException; method public static android.system.StructStat lstat(java.lang.String) throws android.system.ErrnoException; method public static void mincore(long, long, byte[]) throws android.system.ErrnoException; @@ -35425,6 +35427,7 @@ package android.system { method public static int recvfrom(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static int recvfrom(java.io.FileDescriptor, byte[], int, int, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static void remove(java.lang.String) throws android.system.ErrnoException; + method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException; method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException; method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException; method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; @@ -35436,6 +35439,7 @@ package android.system { method public static int setsid() throws android.system.ErrnoException; method public static void setsockoptInt(java.io.FileDescriptor, int, int, int) throws android.system.ErrnoException; method public static void setuid(int) throws android.system.ErrnoException; + method public static void setxattr(java.lang.String, java.lang.String, byte[], int) throws android.system.ErrnoException; method public static void shutdown(java.io.FileDescriptor, int) throws android.system.ErrnoException; method public static java.io.FileDescriptor socket(int, int, int) throws android.system.ErrnoException; method public static void socketpair(int, int, int, java.io.FileDescriptor, java.io.FileDescriptor) throws android.system.ErrnoException; @@ -50581,6 +50585,7 @@ package java.lang { method public boolean isPrimitive(); method public boolean isSynthetic(); method public T newInstance() throws java.lang.IllegalAccessException, java.lang.InstantiationException; + method public java.lang.String toGenericString(); } public class ClassCastException extends java.lang.RuntimeException { @@ -51971,7 +51976,9 @@ package java.lang.reflect { method public abstract java.lang.annotation.Annotation[][] getParameterAnnotations(); method public int getParameterCount(); method public abstract java.lang.Class<?>[] getParameterTypes(); + method public java.lang.reflect.Parameter[] getParameters(); method public abstract java.lang.reflect.TypeVariable<?>[] getTypeParameters(); + method public final boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>); method public boolean isSynthetic(); method public boolean isVarArgs(); method public abstract java.lang.String toGenericString(); @@ -52078,6 +52085,7 @@ package java.lang.reflect { method public static boolean isTransient(int); method public static boolean isVolatile(int); method public static int methodModifiers(); + method public static int parameterModifiers(); method public static java.lang.String toString(int); field public static final int ABSTRACT = 1024; // 0x400 field public static final int FINAL = 16; // 0x10 @@ -52093,6 +52101,21 @@ package java.lang.reflect { field public static final int VOLATILE = 64; // 0x40 } + public final class Parameter implements java.lang.reflect.AnnotatedElement { + method public T getAnnotation(java.lang.Class<T>); + method public java.lang.annotation.Annotation[] getAnnotations(); + method public java.lang.annotation.Annotation[] getDeclaredAnnotations(); + method public java.lang.reflect.Executable getDeclaringExecutable(); + method public int getModifiers(); + method public java.lang.String getName(); + method public java.lang.reflect.Type getParameterizedType(); + method public java.lang.Class<?> getType(); + method public boolean isImplicit(); + method public boolean isNamePresent(); + method public boolean isSynthetic(); + method public boolean isVarArgs(); + } + public abstract interface ParameterizedType implements java.lang.reflect.Type { method public abstract java.lang.reflect.Type[] getActualTypeArguments(); method public abstract java.lang.reflect.Type getOwnerType(); @@ -54855,6 +54878,13 @@ package java.security { method public abstract java.security.ProtectionDomain[] combine(java.security.ProtectionDomain[], java.security.ProtectionDomain[]); } + public final class DomainLoadStoreParameter implements java.security.KeyStore.LoadStoreParameter { + ctor public DomainLoadStoreParameter(java.net.URI, java.util.Map<java.lang.String, java.security.KeyStore.ProtectionParameter>); + method public java.net.URI getConfiguration(); + method public java.security.KeyStore.ProtectionParameter getProtectionParameter(); + method public java.util.Map<java.lang.String, java.security.KeyStore.ProtectionParameter> getProtectionParams(); + } + public class GeneralSecurityException extends java.lang.Exception { ctor public GeneralSecurityException(); ctor public GeneralSecurityException(java.lang.String); @@ -55051,6 +55081,12 @@ package java.security { } public static abstract interface KeyStore.Entry { + method public default java.util.Set<java.security.KeyStore.Entry.Attribute> getAttributes(); + } + + public static abstract interface KeyStore.Entry.Attribute { + method public abstract java.lang.String getName(); + method public abstract java.lang.String getValue(); } public static abstract interface KeyStore.LoadStoreParameter { @@ -55059,11 +55095,15 @@ package java.security { public static class KeyStore.PasswordProtection implements javax.security.auth.Destroyable java.security.KeyStore.ProtectionParameter { ctor public KeyStore.PasswordProtection(char[]); + ctor public KeyStore.PasswordProtection(char[], java.lang.String, java.security.spec.AlgorithmParameterSpec); method public synchronized char[] getPassword(); + method public java.lang.String getProtectionAlgorithm(); + method public java.security.spec.AlgorithmParameterSpec getProtectionParameters(); } public static final class KeyStore.PrivateKeyEntry implements java.security.KeyStore.Entry { ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[]); + ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[], java.util.Set<java.security.KeyStore.Entry.Attribute>); method public java.security.cert.Certificate getCertificate(); method public java.security.cert.Certificate[] getCertificateChain(); method public java.security.PrivateKey getPrivateKey(); @@ -55074,11 +55114,13 @@ package java.security { public static final class KeyStore.SecretKeyEntry implements java.security.KeyStore.Entry { ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey); + ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey, java.util.Set<java.security.KeyStore.Entry.Attribute>); method public javax.crypto.SecretKey getSecretKey(); } public static final class KeyStore.TrustedCertificateEntry implements java.security.KeyStore.Entry { ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate); + ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate, java.util.Set<java.security.KeyStore.Entry.Attribute>); method public java.security.cert.Certificate getTrustedCertificate(); } @@ -55157,6 +55199,14 @@ package java.security { ctor public NoSuchProviderException(java.lang.String); } + public final class PKCS12Attribute implements java.security.KeyStore.Entry.Attribute { + ctor public PKCS12Attribute(java.lang.String, java.lang.String); + ctor public PKCS12Attribute(byte[]); + method public byte[] getEncoded(); + method public java.lang.String getName(); + method public java.lang.String getValue(); + } + public abstract class Permission implements java.security.Guard java.io.Serializable { ctor public Permission(java.lang.String); method public void checkGuard(java.lang.Object) throws java.lang.SecurityException; @@ -55214,10 +55264,11 @@ package java.security { method public abstract boolean equals(java.lang.Object); method public abstract java.lang.String getName(); method public abstract int hashCode(); + method public default boolean implies(javax.security.auth.Subject); method public abstract java.lang.String toString(); } - public abstract interface PrivateKey implements java.security.Key { + public abstract interface PrivateKey implements javax.security.auth.Destroyable java.security.Key { field public static final long serialVersionUID = 6034044314589513430L; // 0x53bd3b559a12c6d6L } @@ -55246,16 +55297,25 @@ package java.security { public abstract class Provider extends java.util.Properties { ctor protected Provider(java.lang.String, double, java.lang.String); + method public synchronized java.lang.Object compute(java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>); + method public synchronized java.lang.Object computeIfAbsent(java.lang.Object, java.util.function.Function<? super java.lang.Object, ? extends java.lang.Object>); + method public synchronized java.lang.Object computeIfPresent(java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>); method public synchronized void forEach(java.util.function.BiConsumer<? super java.lang.Object, ? super java.lang.Object>); method public java.lang.String getInfo(); method public java.lang.String getName(); + method public synchronized java.lang.Object getOrDefault(java.lang.Object, java.lang.Object); method public synchronized java.security.Provider.Service getService(java.lang.String, java.lang.String); method public synchronized java.util.Set<java.security.Provider.Service> getServices(); method public double getVersion(); + method public synchronized java.lang.Object merge(java.lang.Object, java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>); method public synchronized java.lang.Object put(java.lang.Object, java.lang.Object); method public synchronized void putAll(java.util.Map<?, ?>); + method public synchronized java.lang.Object putIfAbsent(java.lang.Object, java.lang.Object); method protected synchronized void putService(java.security.Provider.Service); method protected synchronized void removeService(java.security.Provider.Service); + method public synchronized boolean replace(java.lang.Object, java.lang.Object, java.lang.Object); + method public synchronized java.lang.Object replace(java.lang.Object, java.lang.Object); + method public synchronized void replaceAll(java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>); } public static class Provider.Service { @@ -55297,6 +55357,7 @@ package java.security { method public static java.security.SecureRandom getInstance(java.lang.String) throws java.security.NoSuchAlgorithmException; method public static java.security.SecureRandom getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException; method public static java.security.SecureRandom getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException; + method public static java.security.SecureRandom getInstanceStrong() throws java.security.NoSuchAlgorithmException; method public final java.security.Provider getProvider(); method public static byte[] getSeed(int); method protected final int next(int); @@ -55915,6 +55976,7 @@ package java.security.cert { method public abstract int getVersion(); method public abstract void verify(java.security.PublicKey) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException; method public abstract void verify(java.security.PublicKey, java.lang.String) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException; + method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException; } public abstract class X509CRLEntry implements java.security.cert.X509Extension { @@ -58362,6 +58424,33 @@ package java.util { method public static java.lang.String toString(java.lang.Object[]); } + public class Base64 { + method public static java.util.Base64.Decoder getDecoder(); + method public static java.util.Base64.Encoder getEncoder(); + method public static java.util.Base64.Decoder getMimeDecoder(); + method public static java.util.Base64.Encoder getMimeEncoder(); + method public static java.util.Base64.Encoder getMimeEncoder(int, byte[]); + method public static java.util.Base64.Decoder getUrlDecoder(); + method public static java.util.Base64.Encoder getUrlEncoder(); + } + + public static class Base64.Decoder { + method public byte[] decode(byte[]); + method public byte[] decode(java.lang.String); + method public int decode(byte[], byte[]); + method public java.nio.ByteBuffer decode(java.nio.ByteBuffer); + method public java.io.InputStream wrap(java.io.InputStream); + } + + public static class Base64.Encoder { + method public byte[] encode(byte[]); + method public int encode(byte[], byte[]); + method public java.nio.ByteBuffer encode(java.nio.ByteBuffer); + method public java.lang.String encodeToString(byte[]); + method public java.util.Base64.Encoder withoutPadding(); + method public java.io.OutputStream wrap(java.io.OutputStream); + } + public class BitSet implements java.lang.Cloneable java.io.Serializable { ctor public BitSet(); ctor public BitSet(int); diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 80af5ea9fdaf..18ad43eac37e 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -185,12 +185,7 @@ static const char ZYGOTE_NICE_NAME[] = "zygote"; int main(int argc, char* const argv[]) { if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { - // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return - // EINVAL. Don't die on such kernels. - if (errno != EINVAL) { - LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno)); - return 12; - } + LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno)); } AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); @@ -309,6 +304,5 @@ int main(int argc, char* const argv[]) fprintf(stderr, "Error: no class name or --zygote supplied.\n"); app_usage(); LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); - return 10; } } diff --git a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java index 17e533a5b32f..da815691ca0e 100644 --- a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java +++ b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java @@ -83,11 +83,6 @@ public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { } @Override - public void onMultiAdvertiseCallback(int status, boolean isStart, - AdvertiseSettings advertiseSettings) throws RemoteException { - } - - @Override public void onConfigureMTU(String address, int mtu, int status) throws RemoteException { } diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl index 74980385a604..f4ebcaff4ddc 100644 --- a/core/java/android/bluetooth/IBluetoothGatt.aidl +++ b/core/java/android/bluetooth/IBluetoothGatt.aidl @@ -28,6 +28,7 @@ import android.os.WorkSource; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; +import android.bluetooth.le.IAdvertiserCallback; /** * API for interacting with BLE / GATT @@ -41,11 +42,15 @@ interface IBluetoothGatt { in String callingPackage); void stopScan(in int appIf, in boolean isServer); void flushPendingBatchResults(in int appIf, in boolean isServer); - void startMultiAdvertising(in int appIf, + + void registerAdvertiser(in IAdvertiserCallback callback); + void unregisterAdvertiser(in int advertiserId); + void startMultiAdvertising(in int advertiserId, in AdvertiseData advertiseData, in AdvertiseData scanResponse, in AdvertiseSettings settings); - void stopMultiAdvertising(in int appIf); + void stopMultiAdvertising(in int advertiserId); + void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl index 7163c370694c..efda08e23634 100644 --- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -38,8 +38,6 @@ oneway interface IBluetoothGattCallback { void onDescriptorWrite(in String address, in int status, in int handle); void onNotify(in String address, in int handle, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); - void onMultiAdvertiseCallback(in int status, boolean isStart, - in AdvertiseSettings advertiseSettings); void onScanManagerErrorCallback(in int errorCode); void onConfigureMTU(in String address, in int mtu, in int status); void onFoundOrLost(in boolean onFound, in ScanResult scanResult); diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java index d468bd416eda..048f7910cdea 100644 --- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -22,6 +22,7 @@ import android.bluetooth.BluetoothGattCallbackWrapper; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; +import android.bluetooth.le.IAdvertiserCallback; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; @@ -162,7 +163,7 @@ public final class BluetoothLeAdvertiser { } /** - * Cleans up advertise clients. Should be called when bluetooth is down. + * Cleans up advertisers. Should be called when bluetooth is down. * * @hide */ @@ -228,7 +229,7 @@ public final class BluetoothLeAdvertiser { /** * Bluetooth GATT interface callbacks for advertising. */ - private class AdvertiseCallbackWrapper extends BluetoothGattCallbackWrapper { + private class AdvertiseCallbackWrapper extends IAdvertiserCallback.Stub { private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; private final AdvertiseCallback mAdvertiseCallback; private final AdvertiseData mAdvertisement; @@ -236,10 +237,10 @@ public final class BluetoothLeAdvertiser { private final AdvertiseSettings mSettings; private final IBluetoothGatt mBluetoothGatt; - // mClientIf 0: not registered + // mAdvertiserId 0: not registered // -1: advertise stopped or registration timeout // >0: registered and advertising started - private int mClientIf; + private int mAdvertiserId; private boolean mIsAdvertising = false; public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, @@ -251,35 +252,34 @@ public final class BluetoothLeAdvertiser { mScanResponse = scanResponse; mSettings = settings; mBluetoothGatt = bluetoothGatt; - mClientIf = 0; + mAdvertiserId = 0; } public void startRegisteration() { synchronized (this) { - if (mClientIf == -1) return; + if (mAdvertiserId == -1) return; try { - UUID uuid = UUID.randomUUID(); - mBluetoothGatt.registerClient(new ParcelUuid(uuid), this); + mBluetoothGatt.registerAdvertiser(this); wait(LE_CALLBACK_TIMEOUT_MILLIS); } catch (InterruptedException | RemoteException e) { Log.e(TAG, "Failed to start registeration", e); } - if (mClientIf > 0 && mIsAdvertising) { + if (mAdvertiserId > 0 && mIsAdvertising) { mLeAdvertisers.put(mAdvertiseCallback, this); - } else if (mClientIf <= 0) { + } else if (mAdvertiserId <= 0) { // Registration timeout, reset mClientIf to -1 so no subsequent operations can // proceed. - if (mClientIf == 0) mClientIf = -1; + if (mAdvertiserId == 0) mAdvertiserId = -1; // Post internal error if registration failed. postStartFailure(mAdvertiseCallback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); } else { // Unregister application if it's already registered but advertise failed. try { - mBluetoothGatt.unregisterClient(mClientIf); - mClientIf = -1; + mBluetoothGatt.unregisterAdvertiser(mAdvertiserId); + mAdvertiserId = -1; } catch (RemoteException e) { Log.e(TAG, "remote exception when unregistering", e); } @@ -290,7 +290,7 @@ public final class BluetoothLeAdvertiser { public void stopAdvertising() { synchronized (this) { try { - mBluetoothGatt.stopMultiAdvertising(mClientIf); + mBluetoothGatt.stopMultiAdvertising(mAdvertiserId); wait(LE_CALLBACK_TIMEOUT_MILLIS); } catch (InterruptedException | RemoteException e) { Log.e(TAG, "Failed to stop advertising", e); @@ -305,20 +305,20 @@ public final class BluetoothLeAdvertiser { } /** - * Application interface registered - app is ready to go + * Advertiser interface registered - app is ready to go */ @Override - public void onClientRegistered(int status, int clientIf) { - Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); + public void onAdvertiserRegistered(int status, int advertiserId) { + Log.d(TAG, "onAdvertiserRegistered() - status=" + status + " advertiserId=" + advertiserId); synchronized (this) { if (status == BluetoothGatt.GATT_SUCCESS) { try { - if (mClientIf == -1) { - // Registration succeeds after timeout, unregister client. - mBluetoothGatt.unregisterClient(clientIf); + if (mAdvertiserId == -1) { + // Registration succeeds after timeout, unregister advertiser. + mBluetoothGatt.unregisterAdvertiser(advertiserId); } else { - mClientIf = clientIf; - mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement, + mAdvertiserId = advertiserId; + mBluetoothGatt.startMultiAdvertising(mAdvertiserId, mAdvertisement, mScanResponse, mSettings); } return; @@ -327,7 +327,7 @@ public final class BluetoothLeAdvertiser { } } // Registration failed. - mClientIf = -1; + mAdvertiserId = -1; notifyAll(); } } @@ -346,10 +346,10 @@ public final class BluetoothLeAdvertiser { postStartFailure(mAdvertiseCallback, status); } } else { - // unregister client for stop. + // unregister advertiser for stop. try { - mBluetoothGatt.unregisterClient(mClientIf); - mClientIf = -1; + mBluetoothGatt.unregisterAdvertiser(mAdvertiserId); + mAdvertiserId = -1; mIsAdvertising = false; mLeAdvertisers.remove(mAdvertiseCallback); } catch (RemoteException e) { diff --git a/core/java/android/bluetooth/le/IAdvertiserCallback.aidl b/core/java/android/bluetooth/le/IAdvertiserCallback.aidl new file mode 100644 index 000000000000..c58b1dfec965 --- /dev/null +++ b/core/java/android/bluetooth/le/IAdvertiserCallback.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.bluetooth.le; + +import android.bluetooth.le.AdvertiseSettings; + +/** + * Callback definitions for interacting with Advertiser + * @hide + */ +oneway interface IAdvertiserCallback { + void onAdvertiserRegistered(in int status, in int advertiserId); + + void onMultiAdvertiseCallback(in int status, boolean isStart, + in AdvertiseSettings advertiseSettings); +} diff --git a/core/java/android/content/pm/IOtaDexopt.aidl b/core/java/android/content/pm/IOtaDexopt.aidl index 8f38d6f90a7d..467bd5f5e82b 100644 --- a/core/java/android/content/pm/IOtaDexopt.aidl +++ b/core/java/android/content/pm/IOtaDexopt.aidl @@ -42,8 +42,21 @@ interface IOtaDexopt { boolean isDone(); /** + * Return the progress (0..1) made in this session. When {@link #isDone() isDone} returns + * true, the progress value will be 1. + */ + float getProgress(); + + /** * Optimize the next package. Note: this command is synchronous, that is, only returns after * the package has been dexopted (or dexopting failed). + * + * Note: this will be removed after a transition period. Use nextDexoptCommand instead. */ void dexoptNextPackage(); + + /** + * Get the optimization parameters for the next package. + */ + String nextDexoptCommand(); } diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 6162d1aeeceb..693228f4cd13 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -347,15 +347,17 @@ public abstract class RegisteredServicesCache<V> { @VisibleForTesting protected boolean inSystemImage(int callerUid) { String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid); - for (String name : packages) { - try { - PackageInfo packageInfo = - mContext.getPackageManager().getPackageInfo(name, 0 /* flags */); - if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - return true; + if (packages != null) { + for (String name : packages) { + try { + PackageInfo packageInfo = + mContext.getPackageManager().getPackageInfo(name, 0 /* flags */); + if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + return true; + } + } catch (PackageManager.NameNotFoundException e) { + return false; } - } catch (PackageManager.NameNotFoundException e) { - return false; } } return false; diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java index 8cdd15305018..ea53a71245a5 100644 --- a/core/java/android/net/InterfaceConfiguration.java +++ b/core/java/android/net/InterfaceConfiguration.java @@ -80,6 +80,14 @@ public class InterfaceConfiguration implements Parcelable { mFlags.add(FLAG_DOWN); } + /** + * Set flags so that no changes will be made to the up/down status. + */ + public void ignoreInterfaceUpDownStatus() { + mFlags.remove(FLAG_UP); + mFlags.remove(FLAG_DOWN); + } + public LinkAddress getLinkAddress() { return mAddr; } diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java index b83fb260d6f1..0f0e9c49a0b0 100644 --- a/core/java/android/net/LocalSocketImpl.java +++ b/core/java/android/net/LocalSocketImpl.java @@ -516,13 +516,11 @@ class LocalSocketImpl Os.setsockoptLinger(fd, OsConstants.SOL_SOCKET, OsConstants.SO_LINGER, linger); break; case SocketOptions.SO_TIMEOUT: - /* - * SO_TIMEOUT from the core library gets converted to - * SO_SNDTIMEO, but the option is supposed to set both - * send and receive timeouts. Note: The incoming timeout - * value is in milliseconds. - */ + // The option must set both send and receive timeouts. + // Note: The incoming timeout value is in milliseconds. StructTimeval timeval = StructTimeval.fromMillis(intValue); + Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO, + timeval); Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO, timeval); break; diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 6243f467016a..ebb9601a759e 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -405,8 +405,15 @@ public final class NetworkCapabilities implements Parcelable { */ public static final int TRANSPORT_VPN = 4; + /** + * Indicates this network uses a Wi-Fi NAN transport. + * + * @hide PROPOSED_NAN_API + */ + public static final int TRANSPORT_WIFI_NAN = 5; + private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR; - private static final int MAX_TRANSPORT = TRANSPORT_VPN; + private static final int MAX_TRANSPORT = TRANSPORT_WIFI_NAN; /** * Adds the given transport type to this {@code NetworkCapability} instance. @@ -862,6 +869,7 @@ public final class NetworkCapabilities implements Parcelable { case TRANSPORT_BLUETOOTH: transports += "BLUETOOTH"; break; case TRANSPORT_ETHERNET: transports += "ETHERNET"; break; case TRANSPORT_VPN: transports += "VPN"; break; + case TRANSPORT_WIFI_NAN: transports += "WIFI_NAN"; break; } if (++i < types.length) transports += "|"; } diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index d8be2b6dcc4c..93fc13c78b5a 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -286,6 +286,11 @@ public class Environment { } /** {@hide} */ + public static File getReferenceProfile(String packageName) { + return buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName); + } + + /** {@hide} */ public static File getDataProfilesDePackageDirectory(int userId, String packageName) { return buildPath(getDataProfilesDeDirectory(userId), packageName); } diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java new file mode 100644 index 000000000000..5ff79f752c39 --- /dev/null +++ b/core/java/android/os/HwBinder.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import libcore.util.NativeAllocationRegistry; + +/** @hide */ +public abstract class HwBinder implements IHwBinder { + private static final String TAG = "HwBinder"; + + private static final NativeAllocationRegistry sNativeRegistry; + + public HwBinder() { + native_setup(); + + sNativeRegistry.registerNativeAllocation( + this, + mNativeContext); + } + + public final native void transact( + int code, HwParcel request, HwParcel reply, int flags); + + public abstract void onTransact( + int code, HwParcel request, HwParcel reply, int flags); + + public native final void registerService(String serviceName); + public static native final IHwBinder getService(String serviceName); + + // Returns address of the "freeFunction". + private static native final long native_init(); + + private native final void native_setup(); + + static { + long freeFunction = native_init(); + + sNativeRegistry = new NativeAllocationRegistry( + HwBinder.class.getClassLoader(), + freeFunction, + 128 /* size */); + } + + private long mNativeContext; +} diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java new file mode 100644 index 000000000000..153c6e634ecb --- /dev/null +++ b/core/java/android/os/HwBlob.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import libcore.util.NativeAllocationRegistry; + +/** @hide */ +public class HwBlob { + private static final String TAG = "HwBlob"; + + private static final NativeAllocationRegistry sNativeRegistry; + + public HwBlob(int size) { + native_setup(size); + + sNativeRegistry.registerNativeAllocation( + this, + mNativeContext); + } + + public native final boolean getBool(long offset); + public native final byte getInt8(long offset); + public native final short getInt16(long offset); + public native final int getInt32(long offset); + public native final long getInt64(long offset); + public native final float getFloat(long offset); + public native final double getDouble(long offset); + public native final String getString(long offset); + + public native final void putBool(long offset, boolean x); + public native final void putInt8(long offset, byte x); + public native final void putInt16(long offset, short x); + public native final void putInt32(long offset, int x); + public native final void putInt64(long offset, long x); + public native final void putFloat(long offset, float x); + public native final void putDouble(long offset, double x); + public native final void putString(long offset, String x); + + public native final void putBlob(long offset, HwBlob blob); + + public native final long handle(); + + // Returns address of the "freeFunction". + private static native final long native_init(); + + private native final void native_setup(int size); + + static { + long freeFunction = native_init(); + + sNativeRegistry = new NativeAllocationRegistry( + HwBlob.class.getClassLoader(), + freeFunction, + 128 /* size */); + } + + private long mNativeContext; +} + + diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java new file mode 100644 index 000000000000..180e8f46e93b --- /dev/null +++ b/core/java/android/os/HwParcel.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import libcore.util.NativeAllocationRegistry; + +/** @hide */ +public class HwParcel { + private static final String TAG = "HwParcel"; + + public static final int STATUS_SUCCESS = 0; + public static final int STATUS_ERROR = -1; + + private static final NativeAllocationRegistry sNativeRegistry; + + private HwParcel(boolean allocate) { + native_setup(allocate); + + sNativeRegistry.registerNativeAllocation( + this, + mNativeContext); + } + + public HwParcel() { + native_setup(true /* allocate */); + + sNativeRegistry.registerNativeAllocation( + this, + mNativeContext); + } + + public native final void writeInterfaceToken(String interfaceName); + public native final void writeBool(boolean val); + public native final void writeInt8(byte val); + public native final void writeInt16(short val); + public native final void writeInt32(int val); + public native final void writeInt64(long val); + public native final void writeFloat(float val); + public native final void writeDouble(double val); + public native final void writeString(String val); + + public native final void writeBoolVector(boolean[] val); + public native final void writeInt8Vector(byte[] val); + public native final void writeInt16Vector(short[] val); + public native final void writeInt32Vector(int[] val); + public native final void writeInt64Vector(long[] val); + public native final void writeFloatVector(float[] val); + public native final void writeDoubleVector(double[] val); + public native final void writeStringVector(String[] val); + + public native final void writeStrongBinder(IHwBinder binder); + + public native final void enforceInterface(String interfaceName); + public native final boolean readBool(); + public native final byte readInt8(); + public native final short readInt16(); + public native final int readInt32(); + public native final long readInt64(); + public native final float readFloat(); + public native final double readDouble(); + public native final String readString(); + + public native final boolean[] readBoolVector(); + public native final byte[] readInt8Vector(); + public native final short[] readInt16Vector(); + public native final int[] readInt32Vector(); + public native final long[] readInt64Vector(); + public native final float[] readFloatVector(); + public native final double[] readDoubleVector(); + public native final String[] readStringVector(); + + public native final IHwBinder readStrongBinder(); + + // Handle is stored as part of the blob. + public native final HwBlob readBuffer(); + + public native final HwBlob readEmbeddedBuffer( + long parentHandle, long offset); + + public native final void writeBuffer(HwBlob blob); + + public native final void writeStatus(int status); + public native final void verifySuccess(); + public native final void releaseTemporaryStorage(); + + public native final void send(); + + // Returns address of the "freeFunction". + private static native final long native_init(); + + private native final void native_setup(boolean allocate); + + static { + long freeFunction = native_init(); + + sNativeRegistry = new NativeAllocationRegistry( + HwParcel.class.getClassLoader(), + freeFunction, + 128 /* size */); + } + + private long mNativeContext; +} + diff --git a/core/java/android/os/HwRemoteBinder.java b/core/java/android/os/HwRemoteBinder.java new file mode 100644 index 000000000000..83866b3cceb7 --- /dev/null +++ b/core/java/android/os/HwRemoteBinder.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import libcore.util.NativeAllocationRegistry; + +/** @hide */ +public class HwRemoteBinder implements IHwBinder { + private static final String TAG = "HwRemoteBinder"; + + private static final NativeAllocationRegistry sNativeRegistry; + + public HwRemoteBinder() { + native_setup_empty(); + + sNativeRegistry.registerNativeAllocation( + this, + mNativeContext); + } + + public IHwInterface queryLocalInterface(String descriptor) { + return null; + } + + public native final void transact( + int code, HwParcel request, HwParcel reply, int flags); + + private static native final long native_init(); + + private native final void native_setup_empty(); + + static { + long freeFunction = native_init(); + + sNativeRegistry = new NativeAllocationRegistry( + HwRemoteBinder.class.getClassLoader(), + freeFunction, + 128 /* size */); + } + + private long mNativeContext; +} diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java new file mode 100644 index 000000000000..76e881eda8af --- /dev/null +++ b/core/java/android/os/IHwBinder.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** @hide */ +public interface IHwBinder { + // These MUST match their corresponding libhwbinder/IBinder.h definition !!! + public static final int FIRST_CALL_TRANSACTION = 1; + public static final int FLAG_ONEWAY = 1; + + public void transact( + int code, HwParcel request, HwParcel reply, int flags); + + public IHwInterface queryLocalInterface(String descriptor); +} diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java new file mode 100644 index 000000000000..7c5ac6f44a49 --- /dev/null +++ b/core/java/android/os/IHwInterface.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** @hide */ +public interface IHwInterface { + public IHwBinder asBinder(); +} diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index f664e70cf7be..6aa9fac8d48b 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -16,34 +16,9 @@ package android.os; -import android.net.LocalSocket; -import android.net.LocalSocketAddress; import android.system.Os; import android.util.Log; -import com.android.internal.os.Zygote; import dalvik.system.VMRuntime; -import java.io.BufferedWriter; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/*package*/ class ZygoteStartFailedEx extends Exception { - ZygoteStartFailedEx(String s) { - super(s); - } - - ZygoteStartFailedEx(Throwable cause) { - super(cause); - } - - ZygoteStartFailedEx(String s, Throwable cause) { - super(s, cause); - } -} /** * Tools for managing OS processes. @@ -387,83 +362,11 @@ public class Process { private static long sStartUptimeMillis; /** - * State for communicating with the zygote process. - * - * @hide for internal use only. - */ - public static class ZygoteState { - final LocalSocket socket; - final DataInputStream inputStream; - final BufferedWriter writer; - final List<String> abiList; - - boolean mClosed; - - private ZygoteState(LocalSocket socket, DataInputStream inputStream, - BufferedWriter writer, List<String> abiList) { - this.socket = socket; - this.inputStream = inputStream; - this.writer = writer; - this.abiList = abiList; - } - - public static ZygoteState connect(String socketAddress) throws IOException { - DataInputStream zygoteInputStream = null; - BufferedWriter zygoteWriter = null; - final LocalSocket zygoteSocket = new LocalSocket(); - - try { - zygoteSocket.connect(new LocalSocketAddress(socketAddress, - LocalSocketAddress.Namespace.RESERVED)); - - zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream()); - - zygoteWriter = new BufferedWriter(new OutputStreamWriter( - zygoteSocket.getOutputStream()), 256); - } catch (IOException ex) { - try { - zygoteSocket.close(); - } catch (IOException ignore) { - } - - throw ex; - } - - String abiListString = getAbiList(zygoteWriter, zygoteInputStream); - Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString); - - return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter, - Arrays.asList(abiListString.split(","))); - } - - boolean matches(String abi) { - return abiList.contains(abi); - } - - public void close() { - try { - socket.close(); - } catch (IOException ex) { - Log.e(LOG_TAG,"I/O exception on routine close", ex); - } - - mClosed = true; - } - - boolean isClosed() { - return mClosed; - } - } - - /** - * The state of the connection to the primary zygote. - */ - static ZygoteState primaryZygoteState; - - /** - * The state of the connection to the secondary zygote. + * State associated with the zygote process. + * @hide */ - static ZygoteState secondaryZygoteState; + public static final ZygoteProcess zygoteProcess = + new ZygoteProcess(ZYGOTE_SOCKET, SECONDARY_ZYGOTE_SOCKET); /** * Start a new process. @@ -509,263 +412,9 @@ public class Process { String instructionSet, String appDataDir, String[] zygoteArgs) { - try { - return startViaZygote(processClass, niceName, uid, gid, gids, + return zygoteProcess.start(processClass, niceName, uid, gid, gids, debugFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, zygoteArgs); - } catch (ZygoteStartFailedEx ex) { - Log.e(LOG_TAG, - "Starting VM process through Zygote failed"); - throw new RuntimeException( - "Starting VM process through Zygote failed", ex); - } - } - - /** retry interval for opening a zygote socket */ - static final int ZYGOTE_RETRY_MILLIS = 500; - - /** - * Queries the zygote for the list of ABIS it supports. - * - * @throws ZygoteStartFailedEx if the query failed. - */ - private static String getAbiList(BufferedWriter writer, DataInputStream inputStream) - throws IOException { - // Each query starts with the argument count (1 in this case) - writer.write("1"); - // ... followed by a new-line. - writer.newLine(); - // ... followed by our only argument. - writer.write("--query-abi-list"); - writer.newLine(); - writer.flush(); - - // The response is a length prefixed stream of ASCII bytes. - int numBytes = inputStream.readInt(); - byte[] bytes = new byte[numBytes]; - inputStream.readFully(bytes); - - return new String(bytes, StandardCharsets.US_ASCII); - } - - /** - * Sends an argument list to the zygote process, which starts a new child - * and returns the child's pid. Please note: the present implementation - * replaces newlines in the argument list with spaces. - * - * @throws ZygoteStartFailedEx if process start failed for any reason - */ - private static ProcessStartResult zygoteSendArgsAndGetResult( - ZygoteState zygoteState, ArrayList<String> args) - throws ZygoteStartFailedEx { - try { - /** - * See com.android.internal.os.ZygoteInit.readArgumentList() - * Presently the wire format to the zygote process is: - * a) a count of arguments (argc, in essence) - * b) a number of newline-separated argument strings equal to count - * - * After the zygote process reads these it will write the pid of - * the child or -1 on failure, followed by boolean to - * indicate whether a wrapper process was used. - */ - final BufferedWriter writer = zygoteState.writer; - final DataInputStream inputStream = zygoteState.inputStream; - - writer.write(Integer.toString(args.size())); - writer.newLine(); - - int sz = args.size(); - for (int i = 0; i < sz; i++) { - String arg = args.get(i); - if (arg.indexOf('\n') >= 0) { - throw new ZygoteStartFailedEx( - "embedded newlines not allowed"); - } - writer.write(arg); - writer.newLine(); - } - - writer.flush(); - - // Should there be a timeout on this? - ProcessStartResult result = new ProcessStartResult(); - result.pid = inputStream.readInt(); - if (result.pid < 0) { - throw new ZygoteStartFailedEx("fork() failed"); - } - result.usingWrapper = inputStream.readBoolean(); - return result; - } catch (IOException ex) { - zygoteState.close(); - throw new ZygoteStartFailedEx(ex); - } - } - - /** - * Starts a new process via the zygote mechanism. - * - * @param processClass Class name whose static main() to run - * @param niceName 'nice' process name to appear in ps - * @param uid a POSIX uid that the new process should setuid() to - * @param gid a POSIX gid that the new process shuold setgid() to - * @param gids null-ok; a list of supplementary group IDs that the - * new process should setgroup() to. - * @param debugFlags Additional flags. - * @param targetSdkVersion The target SDK version for the app. - * @param seInfo null-ok SELinux information for the new process. - * @param abi the ABI the process should use. - * @param instructionSet null-ok the instruction set to use. - * @param appDataDir null-ok the data directory of the app. - * @param extraArgs Additional arguments to supply to the zygote process. - * @return An object that describes the result of the attempt to start the process. - * @throws ZygoteStartFailedEx if process start failed for any reason - */ - private static ProcessStartResult startViaZygote(final String processClass, - final String niceName, - final int uid, final int gid, - final int[] gids, - int debugFlags, int mountExternal, - int targetSdkVersion, - String seInfo, - String abi, - String instructionSet, - String appDataDir, - String[] extraArgs) - throws ZygoteStartFailedEx { - synchronized(Process.class) { - ArrayList<String> argsForZygote = new ArrayList<String>(); - - // --runtime-args, --setuid=, --setgid=, - // and --setgroups= must go first - argsForZygote.add("--runtime-args"); - argsForZygote.add("--setuid=" + uid); - argsForZygote.add("--setgid=" + gid); - if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) { - argsForZygote.add("--enable-jni-logging"); - } - if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) { - argsForZygote.add("--enable-safemode"); - } - if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) { - argsForZygote.add("--enable-debugger"); - } - if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) { - argsForZygote.add("--enable-checkjni"); - } - if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) { - argsForZygote.add("--generate-debug-info"); - } - if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) { - argsForZygote.add("--always-jit"); - } - if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) { - argsForZygote.add("--native-debuggable"); - } - if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) { - argsForZygote.add("--enable-assert"); - } - if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) { - argsForZygote.add("--mount-external-default"); - } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) { - argsForZygote.add("--mount-external-read"); - } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) { - argsForZygote.add("--mount-external-write"); - } - argsForZygote.add("--target-sdk-version=" + targetSdkVersion); - - //TODO optionally enable debuger - //argsForZygote.add("--enable-debugger"); - - // --setgroups is a comma-separated list - if (gids != null && gids.length > 0) { - StringBuilder sb = new StringBuilder(); - sb.append("--setgroups="); - - int sz = gids.length; - for (int i = 0; i < sz; i++) { - if (i != 0) { - sb.append(','); - } - sb.append(gids[i]); - } - - argsForZygote.add(sb.toString()); - } - - if (niceName != null) { - argsForZygote.add("--nice-name=" + niceName); - } - - if (seInfo != null) { - argsForZygote.add("--seinfo=" + seInfo); - } - - if (instructionSet != null) { - argsForZygote.add("--instruction-set=" + instructionSet); - } - - if (appDataDir != null) { - argsForZygote.add("--app-data-dir=" + appDataDir); - } - - argsForZygote.add(processClass); - - if (extraArgs != null) { - for (String arg : extraArgs) { - argsForZygote.add(arg); - } - } - - return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); - } - } - - /** - * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block and retry if the - * zygote is unresponsive. This method is a no-op if a connection is already open. - * - * @hide - */ - public static void establishZygoteConnectionForAbi(String abi) { - try { - openZygoteSocketIfNeeded(abi); - } catch (ZygoteStartFailedEx ex) { - throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex); - } - } - - /** - * Tries to open socket to Zygote process if not already open. If - * already open, does nothing. May block and retry. - */ - private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx { - if (primaryZygoteState == null || primaryZygoteState.isClosed()) { - try { - primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET); - } catch (IOException ioe) { - throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); - } - } - - if (primaryZygoteState.matches(abi)) { - return primaryZygoteState; - } - - // The primary zygote didn't match. Try the secondary. - if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { - try { - secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET); - } catch (IOException ioe) { - throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); - } - } - - if (secondaryZygoteState.matches(abi)) { - return secondaryZygoteState; - } - - throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi); } /** diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index dd7be53d9865..028e30ab93f0 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -695,6 +695,7 @@ public class RecoverySystem { String line = null; int bytesWrittenInMiB = -1, bytesStashedInMiB = -1; int timeTotal = -1; + int uncryptTime = -1; int sourceVersion = -1; while ((line = in.readLine()) != null) { // Here is an example of lines in last_install: @@ -730,6 +731,8 @@ public class RecoverySystem { if (line.startsWith("time")) { timeTotal = scaled; + } else if (line.startsWith("uncrypt_time")) { + uncryptTime = scaled; } else if (line.startsWith("source_build")) { sourceVersion = scaled; } else if (line.startsWith("bytes_written")) { @@ -745,6 +748,9 @@ public class RecoverySystem { if (timeTotal != -1) { MetricsLogger.histogram(context, "ota_time_total", timeTotal); } + if (uncryptTime != -1) { + MetricsLogger.histogram(context, "ota_uncrypt_time", uncryptTime); + } if (sourceVersion != -1) { MetricsLogger.histogram(context, "ota_source_version", sourceVersion); } diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java new file mode 100644 index 000000000000..d7a72960f4c4 --- /dev/null +++ b/core/java/android/os/ZygoteProcess.java @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.net.LocalSocket; +import android.net.LocalSocketAddress; +import android.util.Log; +import com.android.internal.os.Zygote; +import java.io.BufferedWriter; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/*package*/ class ZygoteStartFailedEx extends Exception { + ZygoteStartFailedEx(String s) { + super(s); + } + + ZygoteStartFailedEx(Throwable cause) { + super(cause); + } + + ZygoteStartFailedEx(String s, Throwable cause) { + super(s, cause); + } +} + +/** + * Maintains communication state with the zygote processes. This class is responsible + * for the sockets opened to the zygotes and for starting processes on behalf of the + * {@link android.os.Process} class. + * + * {@hide} + */ +public class ZygoteProcess { + private static final String LOG_TAG = "ZygoteProcess"; + + /** + * The name of the socket used to communicate with the primary zygote. + */ + private final String mSocket; + + /** + * The name of the secondary (alternate ABI) zygote socket. + */ + private final String mSecondarySocket; + + public ZygoteProcess(String primarySocket, String secondarySocket) { + mSocket = primarySocket; + mSecondarySocket = secondarySocket; + } + + /** + * State for communicating with the zygote process. + */ + public static class ZygoteState { + final LocalSocket socket; + final DataInputStream inputStream; + final BufferedWriter writer; + final List<String> abiList; + + boolean mClosed; + + private ZygoteState(LocalSocket socket, DataInputStream inputStream, + BufferedWriter writer, List<String> abiList) { + this.socket = socket; + this.inputStream = inputStream; + this.writer = writer; + this.abiList = abiList; + } + + public static ZygoteState connect(String socketAddress) throws IOException { + DataInputStream zygoteInputStream = null; + BufferedWriter zygoteWriter = null; + final LocalSocket zygoteSocket = new LocalSocket(); + + try { + zygoteSocket.connect(new LocalSocketAddress(socketAddress, + LocalSocketAddress.Namespace.RESERVED)); + + zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream()); + + zygoteWriter = new BufferedWriter(new OutputStreamWriter( + zygoteSocket.getOutputStream()), 256); + } catch (IOException ex) { + try { + zygoteSocket.close(); + } catch (IOException ignore) { + } + + throw ex; + } + + String abiListString = getAbiList(zygoteWriter, zygoteInputStream); + Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString); + + return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter, + Arrays.asList(abiListString.split(","))); + } + + boolean matches(String abi) { + return abiList.contains(abi); + } + + public void close() { + try { + socket.close(); + } catch (IOException ex) { + Log.e(LOG_TAG,"I/O exception on routine close", ex); + } + + mClosed = true; + } + + boolean isClosed() { + return mClosed; + } + } + + /** + * The state of the connection to the primary zygote. + */ + private ZygoteState primaryZygoteState; + + /** + * The state of the connection to the secondary zygote. + */ + private ZygoteState secondaryZygoteState; + + /** + * Start a new process. + * + * <p>If processes are enabled, a new process is created and the + * static main() function of a <var>processClass</var> is executed there. + * The process will continue running after this function returns. + * + * <p>If processes are not enabled, a new thread in the caller's + * process is created and main() of <var>processClass</var> called there. + * + * <p>The niceName parameter, if not an empty string, is a custom name to + * give to the process instead of using processClass. This allows you to + * make easily identifyable processes even if you are using the same base + * <var>processClass</var> to start them. + * + * @param processClass The class to use as the process's main entry + * point. + * @param niceName A more readable name to use for the process. + * @param uid The user-id under which the process will run. + * @param gid The group-id under which the process will run. + * @param gids Additional group-ids associated with the process. + * @param debugFlags Additional flags. + * @param targetSdkVersion The target SDK version for the app. + * @param seInfo null-ok SELinux information for the new process. + * @param abi non-null the ABI this app should be started with. + * @param instructionSet null-ok the instruction set to use. + * @param appDataDir null-ok the data directory of the app. + * @param zygoteArgs Additional arguments to supply to the zygote process. + * + * @return An object that describes the result of the attempt to start the process. + * @throws RuntimeException on fatal start failure + */ + public final Process.ProcessStartResult start(final String processClass, + final String niceName, + int uid, int gid, int[] gids, + int debugFlags, int mountExternal, + int targetSdkVersion, + String seInfo, + String abi, + String instructionSet, + String appDataDir, + String[] zygoteArgs) { + try { + return startViaZygote(processClass, niceName, uid, gid, gids, + debugFlags, mountExternal, targetSdkVersion, seInfo, + abi, instructionSet, appDataDir, zygoteArgs); + } catch (ZygoteStartFailedEx ex) { + Log.e(LOG_TAG, + "Starting VM process through Zygote failed"); + throw new RuntimeException( + "Starting VM process through Zygote failed", ex); + } + } + + /** retry interval for opening a zygote socket */ + static final int ZYGOTE_RETRY_MILLIS = 500; + + /** + * Queries the zygote for the list of ABIS it supports. + * + * @throws ZygoteStartFailedEx if the query failed. + */ + private static String getAbiList(BufferedWriter writer, DataInputStream inputStream) + throws IOException { + // Each query starts with the argument count (1 in this case) + writer.write("1"); + // ... followed by a new-line. + writer.newLine(); + // ... followed by our only argument. + writer.write("--query-abi-list"); + writer.newLine(); + writer.flush(); + + // The response is a length prefixed stream of ASCII bytes. + int numBytes = inputStream.readInt(); + byte[] bytes = new byte[numBytes]; + inputStream.readFully(bytes); + + return new String(bytes, StandardCharsets.US_ASCII); + } + + /** + * Sends an argument list to the zygote process, which starts a new child + * and returns the child's pid. Please note: the present implementation + * replaces newlines in the argument list with spaces. + * + * @throws ZygoteStartFailedEx if process start failed for any reason + */ + private static Process.ProcessStartResult zygoteSendArgsAndGetResult( + ZygoteState zygoteState, ArrayList<String> args) + throws ZygoteStartFailedEx { + try { + /** + * See com.android.internal.os.SystemZygoteInit.readArgumentList() + * Presently the wire format to the zygote process is: + * a) a count of arguments (argc, in essence) + * b) a number of newline-separated argument strings equal to count + * + * After the zygote process reads these it will write the pid of + * the child or -1 on failure, followed by boolean to + * indicate whether a wrapper process was used. + */ + final BufferedWriter writer = zygoteState.writer; + final DataInputStream inputStream = zygoteState.inputStream; + + writer.write(Integer.toString(args.size())); + writer.newLine(); + + int sz = args.size(); + for (int i = 0; i < sz; i++) { + String arg = args.get(i); + if (arg.indexOf('\n') >= 0) { + throw new ZygoteStartFailedEx( + "embedded newlines not allowed"); + } + writer.write(arg); + writer.newLine(); + } + + writer.flush(); + + // Should there be a timeout on this? + Process.ProcessStartResult result = new Process.ProcessStartResult(); + result.pid = inputStream.readInt(); + if (result.pid < 0) { + throw new ZygoteStartFailedEx("fork() failed"); + } + result.usingWrapper = inputStream.readBoolean(); + return result; + } catch (IOException ex) { + zygoteState.close(); + throw new ZygoteStartFailedEx(ex); + } + } + + /** + * Starts a new process via the zygote mechanism. + * + * @param processClass Class name whose static main() to run + * @param niceName 'nice' process name to appear in ps + * @param uid a POSIX uid that the new process should setuid() to + * @param gid a POSIX gid that the new process shuold setgid() to + * @param gids null-ok; a list of supplementary group IDs that the + * new process should setgroup() to. + * @param debugFlags Additional flags. + * @param targetSdkVersion The target SDK version for the app. + * @param seInfo null-ok SELinux information for the new process. + * @param abi the ABI the process should use. + * @param instructionSet null-ok the instruction set to use. + * @param appDataDir null-ok the data directory of the app. + * @param extraArgs Additional arguments to supply to the zygote process. + * @return An object that describes the result of the attempt to start the process. + * @throws ZygoteStartFailedEx if process start failed for any reason + */ + private Process.ProcessStartResult startViaZygote(final String processClass, + final String niceName, + final int uid, final int gid, + final int[] gids, + int debugFlags, int mountExternal, + int targetSdkVersion, + String seInfo, + String abi, + String instructionSet, + String appDataDir, + String[] extraArgs) + throws ZygoteStartFailedEx { + synchronized(Process.class) { + ArrayList<String> argsForZygote = new ArrayList<String>(); + + // --runtime-args, --setuid=, --setgid=, + // and --setgroups= must go first + argsForZygote.add("--runtime-args"); + argsForZygote.add("--setuid=" + uid); + argsForZygote.add("--setgid=" + gid); + if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) { + argsForZygote.add("--enable-jni-logging"); + } + if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) { + argsForZygote.add("--enable-safemode"); + } + if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) { + argsForZygote.add("--enable-debugger"); + } + if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) { + argsForZygote.add("--enable-checkjni"); + } + if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) { + argsForZygote.add("--generate-debug-info"); + } + if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) { + argsForZygote.add("--always-jit"); + } + if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) { + argsForZygote.add("--native-debuggable"); + } + if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) { + argsForZygote.add("--enable-assert"); + } + if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) { + argsForZygote.add("--mount-external-default"); + } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) { + argsForZygote.add("--mount-external-read"); + } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) { + argsForZygote.add("--mount-external-write"); + } + argsForZygote.add("--target-sdk-version=" + targetSdkVersion); + + //TODO optionally enable debuger + //argsForZygote.add("--enable-debugger"); + + // --setgroups is a comma-separated list + if (gids != null && gids.length > 0) { + StringBuilder sb = new StringBuilder(); + sb.append("--setgroups="); + + int sz = gids.length; + for (int i = 0; i < sz; i++) { + if (i != 0) { + sb.append(','); + } + sb.append(gids[i]); + } + + argsForZygote.add(sb.toString()); + } + + if (niceName != null) { + argsForZygote.add("--nice-name=" + niceName); + } + + if (seInfo != null) { + argsForZygote.add("--seinfo=" + seInfo); + } + + if (instructionSet != null) { + argsForZygote.add("--instruction-set=" + instructionSet); + } + + if (appDataDir != null) { + argsForZygote.add("--app-data-dir=" + appDataDir); + } + + argsForZygote.add(processClass); + + if (extraArgs != null) { + for (String arg : extraArgs) { + argsForZygote.add(arg); + } + } + + return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); + } + } + + /** + * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block + * and retry if the zygote is unresponsive. This method is a no-op if a connection is + * already open. + */ + public void establishZygoteConnectionForAbi(String abi) { + try { + openZygoteSocketIfNeeded(abi); + } catch (ZygoteStartFailedEx ex) { + throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex); + } + } + + /** + * Tries to open socket to Zygote process if not already open. If + * already open, does nothing. May block and retry. + */ + private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx { + if (primaryZygoteState == null || primaryZygoteState.isClosed()) { + try { + primaryZygoteState = ZygoteState.connect(mSocket); + } catch (IOException ioe) { + throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); + } + } + + if (primaryZygoteState.matches(abi)) { + return primaryZygoteState; + } + + // The primary zygote didn't match. Try the secondary. + if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { + try { + secondaryZygoteState = ZygoteState.connect(mSecondarySocket); + } catch (IOException ioe) { + throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); + } + } + + if (secondaryZygoteState.matches(abi)) { + return secondaryZygoteState; + } + + throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi); + } +} diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 4013b30fce06..0b7ac88c6925 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2053,12 +2053,10 @@ public final class InputMethodManager { * have any input method subtype. */ public InputMethodSubtype getCurrentInputMethodSubtype() { - synchronized (mH) { - try { - return mService.getCurrentInputMethodSubtype(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + try { + return mService.getCurrentInputMethodSubtype(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index d2174749a286..ad14a2071330 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -307,7 +307,7 @@ public class PowerProfile { private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster"; private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster"; - @SuppressWarnings("deprecated") + @SuppressWarnings("deprecation") private void initCpuClusters() { // Figure out how many CPU clusters we're dealing with final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT); diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index de671b1ab98d..c851e4ec959a 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -230,7 +230,7 @@ public class RuntimeInit { * @param classLoader the classLoader to load {@className} with */ private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader) - throws ZygoteInit.MethodAndArgsCaller { + throws Zygote.MethodAndArgsCaller { Class<?> cl; try { @@ -264,7 +264,7 @@ public class RuntimeInit { * clears up all the stack frames that were required in setting * up the process. */ - throw new ZygoteInit.MethodAndArgsCaller(m, argv); + throw new Zygote.MethodAndArgsCaller(m, argv); } public static final void main(String[] argv) { @@ -301,7 +301,7 @@ public class RuntimeInit { * @param argv arg strings */ public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) - throws ZygoteInit.MethodAndArgsCaller { + throws Zygote.MethodAndArgsCaller { if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote"); Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit"); @@ -324,14 +324,14 @@ public class RuntimeInit { * @param argv arg strings */ public static void wrapperInit(int targetSdkVersion, String[] argv) - throws ZygoteInit.MethodAndArgsCaller { + throws Zygote.MethodAndArgsCaller { if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper"); applicationInit(targetSdkVersion, argv, null); } private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) - throws ZygoteInit.MethodAndArgsCaller { + throws Zygote.MethodAndArgsCaller { // If the application calls System.exit(), terminate the process // immediately without running any shutdown hooks. It is not possible to // shutdown an Android application gracefully. Among other things, the diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java new file mode 100644 index 000000000000..2ed7aa214ccd --- /dev/null +++ b/core/java/com/android/internal/os/WebViewZygoteInit.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +/** + * Startup class for the WebView zygote process. + * + * See {@link ZygoteInit} for generic zygote startup documentation. + * + * @hide + */ +class WebViewZygoteInit { + public static final String TAG = "WebViewZygoteInit"; + + public static void main(String argv[]) { + throw new RuntimeException("Not implemented yet"); + } +} diff --git a/core/java/com/android/internal/os/WifiPowerEstimator.java b/core/java/com/android/internal/os/WifiPowerEstimator.java index 3bd79f7e35a9..d175202163b2 100644 --- a/core/java/com/android/internal/os/WifiPowerEstimator.java +++ b/core/java/com/android/internal/os/WifiPowerEstimator.java @@ -38,13 +38,13 @@ public class WifiPowerEstimator extends PowerCalculator { } /** - * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio. + * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB. */ private static double getWifiPowerPerPacket(PowerProfile profile) { final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system final double WIFI_POWER = profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) / 3600; - return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60); + return WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048); } @Override diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java index c558cf8d1ee7..594b6ab72ef3 100644 --- a/core/java/com/android/internal/os/WrapperInit.java +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -74,14 +74,14 @@ public class WrapperInit { } } - // Mimic Zygote preloading. + // Mimic system Zygote preloading. ZygoteInit.preload(); // Launch the application. String[] runtimeArgs = new String[args.length - 2]; System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length); RuntimeInit.wrapperInit(targetSdkVersion, runtimeArgs); - } catch (ZygoteInit.MethodAndArgsCaller caller) { + } catch (Zygote.MethodAndArgsCaller caller) { caller.run(); } } diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 66cc97598ae8..fc0ccb75de95 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -22,6 +22,9 @@ import dalvik.system.ZygoteHooks; import android.system.ErrnoException; import android.system.Os; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + /** @hide */ public final class Zygote { /* @@ -191,4 +194,39 @@ public final class Zygote { command.append(" '").append(arg.replace("'", "'\\''")).append("'"); } } + + /** + * Helper exception class which holds a method and arguments and + * can call them. This is used as part of a trampoline to get rid of + * the initial process setup stack frames. + */ + public static class MethodAndArgsCaller extends Exception + implements Runnable { + /** method to call */ + private final Method mMethod; + + /** argument array */ + private final String[] mArgs; + + public MethodAndArgsCaller(Method method, String[] args) { + mMethod = method; + mArgs = args; + } + + public void run() { + try { + mMethod.invoke(null, new Object[] { mArgs }); + } catch (IllegalAccessException ex) { + throw new RuntimeException(ex); + } catch (InvocationTargetException ex) { + Throwable cause = ex.getCause(); + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } else if (cause instanceof Error) { + throw (Error) cause; + } + throw new RuntimeException(ex); + } + } + } } diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 85d84bb3f986..132b02236ab9 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -117,7 +117,7 @@ class ZygoteConnection { /** * Reads one start command from the command socket. If successful, - * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller} + * a child is forked and a {@link Zygote.MethodAndArgsCaller} * exception is thrown in that child while in the parent process, * the method returns normally. On failure, the child is not * spawned and messages are printed to the log and stderr. Returns @@ -126,10 +126,10 @@ class ZygoteConnection { * * @return false if command socket should continue to be read from, or * true if an end-of-file has been encountered. - * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main() + * @throws Zygote.MethodAndArgsCaller trampoline to invoke main() * method in child process */ - boolean runOnce() throws ZygoteInit.MethodAndArgsCaller { + boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller { String args[]; Arguments parsedArgs = null; @@ -214,7 +214,7 @@ class ZygoteConnection { fdsToClose[0] = fd.getInt$(); } - fd = ZygoteInit.getServerSocketFileDescriptor(); + fd = zygoteServer.getServerSocketFileDescriptor(); if (fd != null) { fdsToClose[1] = fd.getInt$(); @@ -238,12 +238,13 @@ class ZygoteConnection { try { if (pid == 0) { // in child + zygoteServer.closeServerSocket(); IoUtils.closeQuietly(serverPipeFd); serverPipeFd = null; handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); // should never get here, the child is expected to either - // throw ZygoteInit.MethodAndArgsCaller or exec(). + // throw Zygote.MethodAndArgsCaller or exec(). return true; } else { // in parent...pid of < 0 means failure @@ -712,12 +713,12 @@ class ZygoteConnection { * @param newStderr null-ok; stream to use for stderr until stdio * is reopened. * - * @throws ZygoteInit.MethodAndArgsCaller on success to + * @throws Zygote.MethodAndArgsCaller on success to * trampoline to code that invokes static main. */ private void handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr) - throws ZygoteInit.MethodAndArgsCaller { + throws Zygote.MethodAndArgsCaller { /** * By the time we get here, the native code has closed the two actual Zygote * socket connections, and substituted /dev/null in their place. The LocalSocket @@ -725,8 +726,6 @@ class ZygoteConnection { */ closeSocket(); - ZygoteInit.closeServerSocket(); - if (descriptors != null) { try { Os.dup2(descriptors[0], STDIN_FILENO); diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index b57aea634bee..52b72a4e9991 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -16,7 +16,6 @@ package com.android.internal.os; -import static android.system.OsConstants.POLLIN; import static android.system.OsConstants.S_IRWXG; import static android.system.OsConstants.S_IRWXO; @@ -31,11 +30,11 @@ import android.os.Process; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; +import android.os.ZygoteProcess; import android.security.keystore.AndroidKeyStoreProvider; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; -import android.system.StructPollfd; import android.text.Hyphenator; import android.util.EventLog; import android.util.Log; @@ -52,17 +51,13 @@ import dalvik.system.ZygoteHooks; import libcore.io.IoUtils; import java.io.BufferedReader; -import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.security.Security; import java.security.Provider; -import java.util.ArrayList; /** * Startup class for the zygote process. @@ -82,8 +77,6 @@ public class ZygoteInit { private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload"; private static final String PROPERTY_RUNNING_IN_CONTAINER = "ro.boot.container"; - private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_"; - private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020; private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030; @@ -94,11 +87,8 @@ public class ZygoteInit { private static final String SOCKET_NAME_ARG = "--socket-name="; - private static LocalServerSocket sServerSocket; - /** - * Used to pre-load resources. We hold a global reference on it so it - * never gets destroyed. + * Used to pre-load resources. */ private static Resources mResources; @@ -110,78 +100,6 @@ public class ZygoteInit { /** Controls whether we should preload resources during zygote init. */ public static final boolean PRELOAD_RESOURCES = true; - /** - * Registers a server socket for zygote command connections - * - * @throws RuntimeException when open fails - */ - private static void registerZygoteSocket(String socketName) { - if (sServerSocket == null) { - int fileDesc; - final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; - try { - String env = System.getenv(fullSocketName); - fileDesc = Integer.parseInt(env); - } catch (RuntimeException ex) { - throw new RuntimeException(fullSocketName + " unset or invalid", ex); - } - - try { - FileDescriptor fd = new FileDescriptor(); - fd.setInt$(fileDesc); - sServerSocket = new LocalServerSocket(fd); - } catch (IOException ex) { - throw new RuntimeException( - "Error binding to local socket '" + fileDesc + "'", ex); - } - } - } - - /** - * Waits for and accepts a single command connection. Throws - * RuntimeException on failure. - */ - private static ZygoteConnection acceptCommandPeer(String abiList) { - try { - return new ZygoteConnection(sServerSocket.accept(), abiList); - } catch (IOException ex) { - throw new RuntimeException( - "IOException during accept()", ex); - } - } - - /** - * Close and clean up zygote sockets. Called on shutdown and on the - * child's exit path. - */ - static void closeServerSocket() { - try { - if (sServerSocket != null) { - FileDescriptor fd = sServerSocket.getFileDescriptor(); - sServerSocket.close(); - if (fd != null) { - Os.close(fd); - } - } - } catch (IOException ex) { - Log.e(TAG, "Zygote: error closing sockets", ex); - } catch (ErrnoException ex) { - Log.e(TAG, "Zygote: error closing descriptor", ex); - } - - sServerSocket = null; - } - - /** - * Return the server socket's underlying file descriptor, so that - * ZygoteConnection can pass it to the native code for proper - * closure after a child process is forked off. - */ - - static FileDescriptor getServerSocketFileDescriptor() { - return sServerSocket.getFileDescriptor(); - } - private static final int UNPRIVILEGED_UID = 9999; private static final int UNPRIVILEGED_GID = 9999; @@ -504,9 +422,7 @@ public class ZygoteInit { */ private static void handleSystemServerProcess( ZygoteConnection.Arguments parsedArgs) - throws ZygoteInit.MethodAndArgsCaller { - - closeServerSocket(); + throws Zygote.MethodAndArgsCaller { // set umask to 0077 so new files and directories will default to owner-only permissions. Os.umask(S_IRWXG | S_IRWXO); @@ -609,8 +525,8 @@ public class ZygoteInit { /** * Prepare the arguments and fork for the system server process. */ - private static boolean startSystemServer(String abiList, String socketName) - throws MethodAndArgsCaller, RuntimeException { + private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) + throws Zygote.MethodAndArgsCaller, RuntimeException { long capabilities = posixCapabilitiesAsBits( OsConstants.CAP_IPC_LOCK, OsConstants.CAP_KILL, @@ -666,6 +582,7 @@ public class ZygoteInit { waitForSecondaryZygote(socketName); } + zygoteServer.closeServerSocket(); handleSystemServerProcess(parsedArgs); } @@ -687,10 +604,19 @@ public class ZygoteInit { } public static void main(String argv[]) { + ZygoteServer zygoteServer = new ZygoteServer(); + // Mark zygote start. This ensures that thread creation will throw // an error. ZygoteHooks.startZygoteNoThreadCreation(); + // Zygote goes into its own process group. + try { + Os.setpgid(0, 0); + } catch (ErrnoException ex) { + throw new RuntimeException("Failed to setpgid(0,0)", ex); + } + try { Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit"); RuntimeInit.enableDdms(); @@ -716,7 +642,7 @@ public class ZygoteInit { throw new RuntimeException("No ABI list supplied."); } - registerZygoteSocket(socketName); + zygoteServer.registerServerSocket(socketName); Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygotePreload"); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); @@ -733,8 +659,6 @@ public class ZygoteInit { gcAndFinalize(); Trace.traceEnd(Trace.TRACE_TAG_DALVIK); - Trace.traceEnd(Trace.TRACE_TAG_DALVIK); - // Disable tracing so that forked processes do not inherit stale tracing tags from // Zygote. Trace.setTracingEnabled(false); @@ -745,18 +669,18 @@ public class ZygoteInit { ZygoteHooks.stopZygoteNoThreadCreation(); if (startSystemServer) { - startSystemServer(abiList, socketName); + startSystemServer(abiList, socketName, zygoteServer); } Log.i(TAG, "Accepting command socket connections"); - runSelectLoop(abiList); + zygoteServer.runSelectLoop(abiList); - closeServerSocket(); - } catch (MethodAndArgsCaller caller) { + zygoteServer.closeServerSocket(); + } catch (Zygote.MethodAndArgsCaller caller) { caller.run(); } catch (RuntimeException ex) { - Log.e(TAG, "Zygote died with exception", ex); - closeServerSocket(); + Log.e(TAG, "System zygote died with exception", ex); + zygoteServer.closeServerSocket(); throw ex; } } @@ -777,7 +701,8 @@ public class ZygoteInit { Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET; while (true) { try { - final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName); + final ZygoteProcess.ZygoteState zs = + ZygoteProcess.ZygoteState.connect(otherZygoteName); zs.close(); break; } catch (IOException ioe) { @@ -792,89 +717,8 @@ public class ZygoteInit { } /** - * Runs the zygote process's select loop. Accepts new connections as - * they happen, and reads commands from connections one spawn-request's - * worth at a time. - * - * @throws MethodAndArgsCaller in a child process when a main() should - * be executed. - */ - private static void runSelectLoop(String abiList) throws MethodAndArgsCaller { - ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); - ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); - - fds.add(sServerSocket.getFileDescriptor()); - peers.add(null); - - while (true) { - StructPollfd[] pollFds = new StructPollfd[fds.size()]; - for (int i = 0; i < pollFds.length; ++i) { - pollFds[i] = new StructPollfd(); - pollFds[i].fd = fds.get(i); - pollFds[i].events = (short) POLLIN; - } - try { - Os.poll(pollFds, -1); - } catch (ErrnoException ex) { - throw new RuntimeException("poll failed", ex); - } - for (int i = pollFds.length - 1; i >= 0; --i) { - if ((pollFds[i].revents & POLLIN) == 0) { - continue; - } - if (i == 0) { - ZygoteConnection newPeer = acceptCommandPeer(abiList); - peers.add(newPeer); - fds.add(newPeer.getFileDesciptor()); - } else { - boolean done = peers.get(i).runOnce(); - if (done) { - peers.remove(i); - fds.remove(i); - } - } - } - } - } - - /** * Class not instantiable. */ private ZygoteInit() { } - - /** - * Helper exception class which holds a method and arguments and - * can call them. This is used as part of a trampoline to get rid of - * the initial process setup stack frames. - */ - public static class MethodAndArgsCaller extends Exception - implements Runnable { - /** method to call */ - private final Method mMethod; - - /** argument array */ - private final String[] mArgs; - - public MethodAndArgsCaller(Method method, String[] args) { - mMethod = method; - mArgs = args; - } - - public void run() { - try { - mMethod.invoke(null, new Object[] { mArgs }); - } catch (IllegalAccessException ex) { - throw new RuntimeException(ex); - } catch (InvocationTargetException ex) { - Throwable cause = ex.getCause(); - if (cause instanceof RuntimeException) { - throw (RuntimeException) cause; - } else if (cause instanceof Error) { - throw (Error) cause; - } - throw new RuntimeException(ex); - } - } - } } diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java new file mode 100644 index 000000000000..ab87641006a5 --- /dev/null +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import static android.system.OsConstants.POLLIN; + +import android.net.LocalServerSocket; +import android.system.Os; +import android.system.ErrnoException; +import android.system.StructPollfd; +import android.util.Log; + +import java.io.IOException; +import java.io.FileDescriptor; +import java.util.ArrayList; + +/** + * Server socket class for zygote processes. + * + * Provides functions to wait for commands on a UNIX domain socket, and fork + * off child processes that inherit the initial state of the VM.% + * + * Please see {@link ZygoteConnection.Arguments} for documentation on the + * client protocol. + */ +class ZygoteServer { + public static final String TAG = "ZygoteServer"; + + private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_"; + + private LocalServerSocket mServerSocket; + + ZygoteServer() { + } + + /** + * Registers a server socket for zygote command connections + * + * @throws RuntimeException when open fails + */ + void registerServerSocket(String socketName) { + if (mServerSocket == null) { + int fileDesc; + final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; + try { + String env = System.getenv(fullSocketName); + fileDesc = Integer.parseInt(env); + } catch (RuntimeException ex) { + throw new RuntimeException(fullSocketName + " unset or invalid", ex); + } + + try { + FileDescriptor fd = new FileDescriptor(); + fd.setInt$(fileDesc); + mServerSocket = new LocalServerSocket(fd); + } catch (IOException ex) { + throw new RuntimeException( + "Error binding to local socket '" + fileDesc + "'", ex); + } + } + } + + /** + * Waits for and accepts a single command connection. Throws + * RuntimeException on failure. + */ + private ZygoteConnection acceptCommandPeer(String abiList) { + try { + return new ZygoteConnection(mServerSocket.accept(), abiList); + } catch (IOException ex) { + throw new RuntimeException( + "IOException during accept()", ex); + } + } + + /** + * Close and clean up zygote sockets. Called on shutdown and on the + * child's exit path. + */ + void closeServerSocket() { + try { + if (mServerSocket != null) { + FileDescriptor fd = mServerSocket.getFileDescriptor(); + mServerSocket.close(); + if (fd != null) { + Os.close(fd); + } + } + } catch (IOException ex) { + Log.e(TAG, "Zygote: error closing sockets", ex); + } catch (ErrnoException ex) { + Log.e(TAG, "Zygote: error closing descriptor", ex); + } + + mServerSocket = null; + } + + /** + * Return the server socket's underlying file descriptor, so that + * ZygoteConnection can pass it to the native code for proper + * closure after a child process is forked off. + */ + + FileDescriptor getServerSocketFileDescriptor() { + return mServerSocket.getFileDescriptor(); + } + + /** + * Runs the zygote process's select loop. Accepts new connections as + * they happen, and reads commands from connections one spawn-request's + * worth at a time. + * + * @throws Zygote.MethodAndArgsCaller in a child process when a main() + * should be executed. + */ + void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller { + ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); + ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); + + fds.add(mServerSocket.getFileDescriptor()); + peers.add(null); + + while (true) { + StructPollfd[] pollFds = new StructPollfd[fds.size()]; + for (int i = 0; i < pollFds.length; ++i) { + pollFds[i] = new StructPollfd(); + pollFds[i].fd = fds.get(i); + pollFds[i].events = (short) POLLIN; + } + try { + Os.poll(pollFds, -1); + } catch (ErrnoException ex) { + throw new RuntimeException("poll failed", ex); + } + for (int i = pollFds.length - 1; i >= 0; --i) { + if ((pollFds[i].revents & POLLIN) == 0) { + continue; + } + if (i == 0) { + ZygoteConnection newPeer = acceptCommandPeer(abiList); + peers.add(newPeer); + fds.add(newPeer.getFileDesciptor()); + } else { + boolean done = peers.get(i).runOnce(this); + if (done) { + peers.remove(i); + fds.remove(i); + } + } + } + } + } +} diff --git a/core/java/com/android/internal/util/WakeupMessage.java b/core/java/com/android/internal/util/WakeupMessage.java index 26537451992c..7d222c74ac3b 100644 --- a/core/java/com/android/internal/util/WakeupMessage.java +++ b/core/java/com/android/internal/util/WakeupMessage.java @@ -45,24 +45,32 @@ public class WakeupMessage implements AlarmManager.OnAlarmListener { protected final String mCmdName; @VisibleForTesting protected final int mCmd, mArg1, mArg2; + @VisibleForTesting + protected final Object mObj; private boolean mScheduled; public WakeupMessage(Context context, Handler handler, - String cmdName, int cmd, int arg1, int arg2) { + String cmdName, int cmd, int arg1, int arg2, Object obj) { mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mHandler = handler; mCmdName = cmdName; mCmd = cmd; mArg1 = arg1; mArg2 = arg2; + mObj = obj; } public WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1) { - this(context, handler, cmdName, cmd, arg1, 0); + this(context, handler, cmdName, cmd, arg1, 0, null); + } + + public WakeupMessage(Context context, Handler handler, + String cmdName, int cmd, int arg1, int arg2) { + this(context, handler, cmdName, cmd, arg1, arg2, null); } public WakeupMessage(Context context, Handler handler, String cmdName, int cmd) { - this(context, handler, cmdName, cmd, 0, 0); + this(context, handler, cmdName, cmd, 0, 0, null); } /** @@ -99,7 +107,7 @@ public class WakeupMessage implements AlarmManager.OnAlarmListener { mScheduled = false; } if (stillScheduled) { - Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2); + Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj); mHandler.handleMessage(msg); msg.recycle(); } diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 60e888d17fc0..7e1a0ab06457 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -80,6 +80,10 @@ LOCAL_SRC_FILES:= \ android_text_AndroidBidi.cpp \ android_text_StaticLayout.cpp \ android_os_Debug.cpp \ + android_os_HwBinder.cpp \ + android_os_HwBlob.cpp \ + android_os_HwParcel.cpp \ + android_os_HwRemoteBinder.cpp \ android_os_MemoryFile.cpp \ android_os_MessageQueue.cpp \ android_os_Parcel.cpp \ @@ -177,7 +181,8 @@ LOCAL_SRC_FILES:= \ com_android_internal_os_PathClassLoaderFactory.cpp \ com_android_internal_os_Zygote.cpp \ com_android_internal_util_VirtualRefBasePtr.cpp \ - com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp + com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp \ + hwbinder/EphemeralStorage.cpp \ LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ @@ -260,6 +265,8 @@ LOCAL_SHARED_LIBRARIES := \ libradio_metadata \ libnativeloader \ libmemunreachable \ + libhidl \ + libhwbinder \ LOCAL_SHARED_LIBRARIES += \ libhwui \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 6cbc06cd8847..07392c4055d3 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -156,6 +156,10 @@ extern int register_android_database_SQLiteGlobal(JNIEnv* env); extern int register_android_database_SQLiteDebug(JNIEnv* env); extern int register_android_nio_utils(JNIEnv* env); extern int register_android_os_Debug(JNIEnv* env); +extern int register_android_os_HwBinder(JNIEnv *env); +extern int register_android_os_HwBlob(JNIEnv *env); +extern int register_android_os_HwParcel(JNIEnv *env); +extern int register_android_os_HwRemoteBinder(JNIEnv *env); extern int register_android_os_MessageQueue(JNIEnv* env); extern int register_android_os_Parcel(JNIEnv* env); extern int register_android_os_SELinux(JNIEnv* env); @@ -1287,6 +1291,10 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_SystemProperties), REG_JNI(register_android_os_Binder), REG_JNI(register_android_os_Parcel), + REG_JNI(register_android_os_HwBinder), + REG_JNI(register_android_os_HwBlob), + REG_JNI(register_android_os_HwParcel), + REG_JNI(register_android_os_HwRemoteBinder), REG_JNI(register_android_nio_utils), REG_JNI(register_android_graphics_Canvas), REG_JNI(register_android_graphics_Graphics), diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp new file mode 100644 index 000000000000..7da03143087f --- /dev/null +++ b/core/jni/android_os_HwBinder.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "android_os_HwBinder" +#include <android-base/logging.h> + +#include "android_os_HwBinder.h" + +#include "android_os_HwParcel.h" +#include "android_os_HwRemoteBinder.h" + +#include <JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> +#include <hidl/IServiceManager.h> +#include <hidl/Status.h> +#include <hwbinder/ProcessState.h> +#include <nativehelper/ScopedLocalRef.h> + +#include "core_jni_helpers.h" + +using android::AndroidRuntime; + +#define PACKAGE_PATH "android/os" +#define CLASS_NAME "HwBinder" +#define CLASS_PATH PACKAGE_PATH "/" CLASS_NAME + +namespace android { + +static struct fields_t { + jfieldID contextID; + jmethodID onTransactID; + +} gFields; + +// static +void JHwBinder::InitClass(JNIEnv *env) { + ScopedLocalRef<jclass> clazz( + env, FindClassOrDie(env, CLASS_PATH)); + + gFields.contextID = + GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J"); + + gFields.onTransactID = + GetMethodIDOrDie( + env, + clazz.get(), + "onTransact", + "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V"); +} + +// static +sp<JHwBinder> JHwBinder::SetNativeContext( + JNIEnv *env, jobject thiz, const sp<JHwBinder> &context) { + sp<JHwBinder> old = + (JHwBinder *)env->GetLongField(thiz, gFields.contextID); + + if (context != NULL) { + context->incStrong(NULL /* id */); + } + + if (old != NULL) { + old->decStrong(NULL /* id */); + } + + env->SetLongField(thiz, gFields.contextID, (long)context.get()); + + return old; +} + +// static +sp<JHwBinder> JHwBinder::GetNativeContext( + JNIEnv *env, jobject thiz) { + return (JHwBinder *)env->GetLongField(thiz, gFields.contextID); +} + +JHwBinder::JHwBinder(JNIEnv *env, jobject thiz) { + jclass clazz = env->GetObjectClass(thiz); + CHECK(clazz != NULL); + + mClass = (jclass)env->NewGlobalRef(clazz); + mObject = env->NewWeakGlobalRef(thiz); +} + +JHwBinder::~JHwBinder() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + + env->DeleteGlobalRef(mClass); + mClass = NULL; +} + +status_t JHwBinder::onTransact( + uint32_t code, + const hardware::Parcel &data, + hardware::Parcel *reply, + uint32_t flags, + TransactCallback callback) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + ScopedLocalRef<jobject> requestObj(env, JHwParcel::NewObject(env)); + JHwParcel::GetNativeContext(env, requestObj.get())->setParcel( + const_cast<hardware::Parcel *>(&data), false /* assumeOwnership */); + + ScopedLocalRef<jobject> replyObj(env, JHwParcel::NewObject(env)); + + sp<JHwParcel> replyContext = + JHwParcel::GetNativeContext(env, replyObj.get()); + + replyContext->setParcel(reply, false /* assumeOwnership */); + replyContext->setTransactCallback(callback); + + env->CallVoidMethod( + mObject, + gFields.onTransactID, + code, + requestObj.get(), + replyObj.get(), + flags); + + status_t err = OK; + + if (!replyContext->wasSent()) { + // The implementation never finished the transaction. + err = UNKNOWN_ERROR; // XXX special error code instead? + + reply->setDataPosition(0 /* pos */); + } + + // Release all temporary storage now that scatter-gather data + // has been consolidated, either by calling the TransactCallback, + // if wasSent() == true or clearing the reply parcel (setDataOffset above). + replyContext->getStorage()->release(env); + + // We cannot permanently pass ownership of "data" and "reply" over to their + // Java object wrappers (we don't own them ourselves). + + JHwParcel::GetNativeContext(env, requestObj.get())->setParcel( + NULL /* parcel */, false /* assumeOwnership */); + + replyContext->setParcel( + NULL /* parcel */, false /* assumeOwnership */); + + return err; +} + +} // namespace android + +//////////////////////////////////////////////////////////////////////////////// + +using namespace android; + +static void releaseNativeContext(void *nativeContext) { + sp<JHwBinder> binder = (JHwBinder *)nativeContext; + + if (binder != NULL) { + binder->decStrong(NULL /* id */); + } +} + +static jlong JHwBinder_native_init(JNIEnv *env) { + JHwBinder::InitClass(env); + + return reinterpret_cast<jlong>(&releaseNativeContext); +} + +static void JHwBinder_native_setup(JNIEnv *env, jobject thiz) { + sp<JHwBinder> context = new JHwBinder(env, thiz); + + JHwBinder::SetNativeContext(env, thiz, context); +} + +static void JHwBinder_native_transact( + JNIEnv * /* env */, + jobject /* thiz */, + jint /* code */, + jobject /* requestObj */, + jobject /* replyObj */, + jint /* flags */) { + CHECK(!"Should not be here"); +} + +static void JHwBinder_native_registerService( + JNIEnv *env, jobject thiz, jstring serviceNameObj) { + if (serviceNameObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + const jchar *serviceName = env->GetStringCritical(serviceNameObj, NULL); + + if (serviceName == NULL) { + return; // XXX exception already pending? + } + + const hardware::hidl_version kVersion = hardware::make_hidl_version(1, 0); + + sp<hardware::IBinder> binder = JHwBinder::GetNativeContext(env, thiz); + + status_t err = hardware::defaultServiceManager()->addService( + String16( + reinterpret_cast<const char16_t *>(serviceName), + env->GetStringLength(serviceNameObj)), + binder, + kVersion); + + env->ReleaseStringCritical(serviceNameObj, serviceName); + serviceName = NULL; + + if (err == OK) { + LOG(INFO) << "Starting thread pool."; + ::android::hardware::ProcessState::self()->startThreadPool(); + } + + signalExceptionForError(env, err); +} + +static jobject JHwBinder_native_getService( + JNIEnv *env, jclass /* clazzObj */, jstring serviceNameObj) { + if (serviceNameObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return NULL; + } + + const jchar *serviceName = env->GetStringCritical(serviceNameObj, NULL); + + if (serviceName == NULL) { + return NULL; // XXX exception already pending? + } + + const hardware::hidl_version kVersion = hardware::make_hidl_version(1, 0); + + LOG(INFO) << "looking for service '" + << String8(String16( + reinterpret_cast<const char16_t *>(serviceName), + env->GetStringLength(serviceNameObj))).string() + << "'"; + + sp<hardware::IBinder> service = + hardware::defaultServiceManager()->getService( + String16( + reinterpret_cast<const char16_t *>(serviceName), + env->GetStringLength(serviceNameObj)), + kVersion); + + env->ReleaseStringCritical(serviceNameObj, serviceName); + serviceName = NULL; + + if (service == NULL) { + signalExceptionForError(env, NAME_NOT_FOUND); + return NULL; + } + + return JHwRemoteBinder::NewObject(env, service); +} + +static JNINativeMethod gMethods[] = { + { "native_init", "()J", (void *)JHwBinder_native_init }, + { "native_setup", "()V", (void *)JHwBinder_native_setup }, + + { "transact", + "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V", + (void *)JHwBinder_native_transact }, + + { "registerService", "(Ljava/lang/String;)V", + (void *)JHwBinder_native_registerService }, + + { "getService", "(Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;", + (void *)JHwBinder_native_getService }, +}; + +namespace android { + +int register_android_os_HwBinder(JNIEnv *env) { + return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods)); +} + +} // namespace android diff --git a/core/jni/android_os_HwBinder.h b/core/jni/android_os_HwBinder.h new file mode 100644 index 000000000000..2ebc38164da8 --- /dev/null +++ b/core/jni/android_os_HwBinder.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_OS_HW_BINDER_H +#define _ANDROID_OS_HW_BINDER_H + +#include <android-base/macros.h> +#include <hwbinder/Binder.h> +#include <jni.h> +#include <utils/RefBase.h> + +namespace android { + +struct JHwBinder : public hardware::BBinder { + static void InitClass(JNIEnv *env); + + static sp<JHwBinder> SetNativeContext( + JNIEnv *env, jobject thiz, const sp<JHwBinder> &context); + + static sp<JHwBinder> GetNativeContext(JNIEnv *env, jobject thiz); + + JHwBinder(JNIEnv *env, jobject thiz); + +protected: + virtual ~JHwBinder(); + + virtual status_t onTransact( + uint32_t code, + const hardware::Parcel &data, + hardware::Parcel *reply, + uint32_t flags, + TransactCallback callback); + +private: + jclass mClass; + jobject mObject; + + DISALLOW_COPY_AND_ASSIGN(JHwBinder); +}; + +int register_android_os_HwBinder(JNIEnv *env); + +} // namespace android + +#endif // _ANDROID_OS_HW_BINDER_H + + diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp new file mode 100644 index 000000000000..b2dee0689ee0 --- /dev/null +++ b/core/jni/android_os_HwBlob.cpp @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "android_os_HwBlob" +#include <android-base/logging.h> + +#include "android_os_HwBlob.h" + +#include "android_os_HwParcel.h" + +#include <JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> +#include <hidl/Status.h> +#include <nativehelper/ScopedLocalRef.h> + +#include "core_jni_helpers.h" + +using android::AndroidRuntime; +using android::hardware::hidl_string; + +#define PACKAGE_PATH "android/os" +#define CLASS_NAME "HwBlob" +#define CLASS_PATH PACKAGE_PATH "/" CLASS_NAME + +namespace android { + +static struct fields_t { + jfieldID contextID; + jmethodID constructID; + +} gFields; + +// static +void JHwBlob::InitClass(JNIEnv *env) { + ScopedLocalRef<jclass> clazz( + env, FindClassOrDie(env, CLASS_PATH)); + + gFields.contextID = + GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J"); + + gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "(I)V"); +} + +// static +sp<JHwBlob> JHwBlob::SetNativeContext( + JNIEnv *env, jobject thiz, const sp<JHwBlob> &context) { + sp<JHwBlob> old = (JHwBlob *)env->GetLongField(thiz, gFields.contextID); + + if (context != NULL) { + context->incStrong(NULL /* id */); + } + + if (old != NULL) { + old->decStrong(NULL /* id */); + } + + env->SetLongField(thiz, gFields.contextID, (long)context.get()); + + return old; +} + +// static +sp<JHwBlob> JHwBlob::GetNativeContext(JNIEnv *env, jobject thiz) { + return (JHwBlob *)env->GetLongField(thiz, gFields.contextID); +} + +JHwBlob::JHwBlob(JNIEnv *env, jobject thiz, size_t size) + : mBuffer(nullptr), + mSize(size), + mOwnsBuffer(true), + mHandle(0) { + jclass clazz = env->GetObjectClass(thiz); + CHECK(clazz != NULL); + + mClass = (jclass)env->NewGlobalRef(clazz); + mObject = env->NewWeakGlobalRef(thiz); + + if (size > 0) { + mBuffer = malloc(size); + } +} + +JHwBlob::~JHwBlob() { + if (mOwnsBuffer) { + free(mBuffer); + mBuffer = nullptr; + } + + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + + env->DeleteGlobalRef(mClass); + mClass = NULL; +} + +void JHwBlob::setTo(const void *ptr, size_t handle) { + CHECK_EQ(mSize, 0u); + CHECK(mBuffer == nullptr); + + mBuffer = const_cast<void *>(ptr); + mSize = SIZE_MAX; // XXX + mOwnsBuffer = false; + mHandle = handle; +} + +status_t JHwBlob::getHandle(size_t *handle) const { + if (mOwnsBuffer) { + return INVALID_OPERATION; + } + + *handle = mHandle; + + return OK; +} + +status_t JHwBlob::read(size_t offset, void *data, size_t size) const { + if (offset + size > mSize) { + return -ERANGE; + } + + memcpy(data, (const uint8_t *)mBuffer + offset, size); + + return OK; +} + +status_t JHwBlob::write(size_t offset, const void *data, size_t size) { + if (offset + size > mSize) { + return -ERANGE; + } + + memcpy((uint8_t *)mBuffer + offset, data, size); + + return OK; +} + +status_t JHwBlob::getString(size_t offset, const hidl_string **s) const { + if ((offset + sizeof(hidl_string)) > mSize) { + return -ERANGE; + } + + *s = reinterpret_cast<const hidl_string *>( + (const uint8_t *)mBuffer + offset); + + return OK; +} + +const void *JHwBlob::data() const { + return mBuffer; +} + +size_t JHwBlob::size() const { + return mSize; +} + +status_t JHwBlob::putBlob(size_t offset, const sp<JHwBlob> &blob) { + size_t index = mSubBlobs.add(); + BlobInfo *info = &mSubBlobs.editItemAt(index); + + info->mOffset = offset; + info->mBlob = blob; + + const void *data = blob->data(); + + return write(offset, &data, sizeof(data)); +} + +status_t JHwBlob::writeToParcel(hardware::Parcel *parcel) const { + size_t handle; + status_t err = parcel->writeBuffer(data(), size(), &handle); + + if (err != OK) { + return err; + } + + for (size_t i = 0; i < mSubBlobs.size(); ++i) { + const BlobInfo &info = mSubBlobs[i]; + + err = info.mBlob->writeEmbeddedToParcel(parcel, handle, info.mOffset); + + if (err != OK) { + return err; + } + } + + return OK; +} + +status_t JHwBlob::writeEmbeddedToParcel( + hardware::Parcel *parcel, + size_t parentHandle, + size_t parentOffset) const { + size_t handle; + status_t err = parcel->writeEmbeddedBuffer( + data(), size(), &handle, parentHandle, parentOffset); + + if (err != OK) { + return err; + } + + for (size_t i = 0; i < mSubBlobs.size(); ++i) { + const BlobInfo &info = mSubBlobs[i]; + + err = info.mBlob->writeEmbeddedToParcel(parcel, handle, info.mOffset); + + if (err != OK) { + return err; + } + } + + return OK; +} + +// static +jobject JHwBlob::NewObject(JNIEnv *env, const void *ptr, size_t handle) { + jobject obj = JHwBlob::NewObject(env, 0 /* size */); + JHwBlob::GetNativeContext(env, obj)->setTo(ptr, handle); + + return obj; +} + +// static +jobject JHwBlob::NewObject(JNIEnv *env, size_t size) { + ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH)); + + jmethodID constructID = + GetMethodIDOrDie(env, clazz.get(), "<init>", "(I)V"); + + // XXX Again cannot refer to gFields.constructID because InitClass may + // not have been called yet. + + return env->NewObject(clazz.get(), constructID, size); +} + +} // namespace android + +//////////////////////////////////////////////////////////////////////////////// + +using namespace android; + +static void releaseNativeContext(void *nativeContext) { + sp<JHwBlob> parcel = (JHwBlob *)nativeContext; + + if (parcel != NULL) { + parcel->decStrong(NULL /* id */); + } +} + +static jlong JHwBlob_native_init(JNIEnv *env) { + JHwBlob::InitClass(env); + + return reinterpret_cast<jlong>(&releaseNativeContext); +} + +static void JHwBlob_native_setup( + JNIEnv *env, jobject thiz, jint size) { + sp<JHwBlob> context = new JHwBlob(env, thiz, size); + + JHwBlob::SetNativeContext(env, thiz, context); +} + +#define DEFINE_BLOB_GETTER(Suffix,Type) \ +static Type JHwBlob_native_get ## Suffix( \ + JNIEnv *env, jobject thiz, jlong offset) { \ + sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \ + \ + Type x; \ + status_t err = blob->read(offset, &x, sizeof(x)); \ + \ + if (err != OK) { \ + signalExceptionForError(env, err); \ + return 0; \ + } \ + \ + return x; \ +} + +DEFINE_BLOB_GETTER(Int8,jbyte) +DEFINE_BLOB_GETTER(Int16,jshort) +DEFINE_BLOB_GETTER(Int32,jint) +DEFINE_BLOB_GETTER(Int64,jlong) +DEFINE_BLOB_GETTER(Float,jfloat) +DEFINE_BLOB_GETTER(Double,jdouble) + +static jboolean JHwBlob_native_getBool( + JNIEnv *env, jobject thiz, jlong offset) { + sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); + + bool x; + status_t err = blob->read(offset, &x, sizeof(x)); + + if (err != OK) { + signalExceptionForError(env, err); + return 0; + } + + return (jboolean)x; +} + +static jstring JHwBlob_native_getString( + JNIEnv *env, jobject thiz, jlong offset) { + sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); + + const hidl_string *s; + status_t err = blob->getString(offset, &s); + + if (err != OK) { + signalExceptionForError(env, err); + return nullptr; + } + + return env->NewStringUTF(s->c_str()); +} + +#define DEFINE_BLOB_PUTTER(Suffix,Type) \ +static void JHwBlob_native_put ## Suffix( \ + JNIEnv *env, jobject thiz, jlong offset, Type x) { \ + \ + sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \ + \ + status_t err = blob->write(offset, &x, sizeof(x)); \ + \ + if (err != OK) { \ + signalExceptionForError(env, err); \ + } \ +} + +DEFINE_BLOB_PUTTER(Int8,jbyte) +DEFINE_BLOB_PUTTER(Int16,jshort) +DEFINE_BLOB_PUTTER(Int32,jint) +DEFINE_BLOB_PUTTER(Int64,jlong) +DEFINE_BLOB_PUTTER(Float,jfloat) +DEFINE_BLOB_PUTTER(Double,jdouble) + +static void JHwBlob_native_putBool( + JNIEnv *env, jobject thiz, jlong offset, jboolean x) { + + sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); + + bool b = (bool)x; + status_t err = blob->write(offset, &b, sizeof(b)); + + if (err != OK) { + signalExceptionForError(env, err); + } +} + +static void JHwBlob_native_putString( + JNIEnv *env, jobject thiz, jlong offset, jstring stringObj) { + if (stringObj == nullptr) { + jniThrowException(env, "java/lang/NullPointerException", nullptr); + return; + } + + const char *s = env->GetStringUTFChars(stringObj, nullptr); + + if (s == nullptr) { + return; + } + + size_t size = strlen(s) + 1; + ScopedLocalRef<jobject> subBlobObj(env, JHwBlob::NewObject(env, size)); + sp<JHwBlob> subBlob = JHwBlob::GetNativeContext(env, subBlobObj.get()); + subBlob->write(0 /* offset */, s, size); + + env->ReleaseStringUTFChars(stringObj, s); + s = nullptr; + + hidl_string tmp; + tmp.setToExternal(static_cast<const char *>(subBlob->data()), size); + + sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); + blob->write(offset, &tmp, sizeof(tmp)); + blob->putBlob(offset + hidl_string::kOffsetOfBuffer, subBlob); +} + +static void JHwBlob_native_putBlob( + JNIEnv *env, jobject thiz, jlong offset, jobject blobObj) { + if (blobObj == nullptr) { + jniThrowException(env, "java/lang/NullPointerException", nullptr); + return; + } + + sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); + sp<JHwBlob> subBlob = JHwBlob::GetNativeContext(env, blobObj); + + blob->putBlob(offset, subBlob); +} + +static jlong JHwBlob_native_handle(JNIEnv *env, jobject thiz) { + size_t handle; + status_t err = JHwBlob::GetNativeContext(env, thiz)->getHandle(&handle); + + if (err != OK) { + signalExceptionForError(env, err); + return 0; + } + + return handle; +} + +static JNINativeMethod gMethods[] = { + { "native_init", "()J", (void *)JHwBlob_native_init }, + { "native_setup", "(I)V", (void *)JHwBlob_native_setup }, + + { "getBool", "(J)Z", (void *)JHwBlob_native_getBool }, + { "getInt8", "(J)B", (void *)JHwBlob_native_getInt8 }, + { "getInt16", "(J)S", (void *)JHwBlob_native_getInt16 }, + { "getInt32", "(J)I", (void *)JHwBlob_native_getInt32 }, + { "getInt64", "(J)J", (void *)JHwBlob_native_getInt64 }, + { "getFloat", "(J)F", (void *)JHwBlob_native_getFloat }, + { "getDouble", "(J)D", (void *)JHwBlob_native_getDouble }, + { "getString", "(J)Ljava/lang/String;", (void *)JHwBlob_native_getString }, + + { "putBool", "(JZ)V", (void *)JHwBlob_native_putBool }, + { "putInt8", "(JB)V", (void *)JHwBlob_native_putInt8 }, + { "putInt16", "(JS)V", (void *)JHwBlob_native_putInt16 }, + { "putInt32", "(JI)V", (void *)JHwBlob_native_putInt32 }, + { "putInt64", "(JJ)V", (void *)JHwBlob_native_putInt64 }, + { "putFloat", "(JF)V", (void *)JHwBlob_native_putFloat }, + { "putDouble", "(JD)V", (void *)JHwBlob_native_putDouble }, + { "putString", "(JLjava/lang/String;)V", (void *)JHwBlob_native_putString }, + + { "putBlob", "(JL" PACKAGE_PATH "/HwBlob;)V", + (void *)JHwBlob_native_putBlob }, + + { "handle", "()J", (void *)JHwBlob_native_handle }, +}; + +namespace android { + +int register_android_os_HwBlob(JNIEnv *env) { + return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods)); +} + +} // namespace android + diff --git a/core/jni/android_os_HwBlob.h b/core/jni/android_os_HwBlob.h new file mode 100644 index 000000000000..6bd82e98e911 --- /dev/null +++ b/core/jni/android_os_HwBlob.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_OS_HW_BLOB_H +#define ANDROID_OS_HW_BLOB_H + +#include <android-base/macros.h> +#include <jni.h> +#include <hidl/HidlSupport.h> +#include <utils/RefBase.h> +#include <utils/Vector.h> + +namespace android { + +struct JHwBlob : public RefBase { + static void InitClass(JNIEnv *env); + + static sp<JHwBlob> SetNativeContext( + JNIEnv *env, jobject thiz, const sp<JHwBlob> &context); + + static sp<JHwBlob> GetNativeContext(JNIEnv *env, jobject thiz); + + static jobject NewObject(JNIEnv *env, const void *ptr, size_t handle); + static jobject NewObject(JNIEnv *env, size_t size); + + JHwBlob(JNIEnv *env, jobject thiz, size_t size); + + void setTo(const void *ptr, size_t handle); + + status_t getHandle(size_t *handle) const; + + status_t read(size_t offset, void *data, size_t size) const; + status_t write(size_t offset, const void *data, size_t size); + + status_t getString( + size_t offset, const android::hardware::hidl_string **s) const; + + const void *data() const; + size_t size() const; + + status_t putBlob(size_t offset, const sp<JHwBlob> &blob); + + status_t writeToParcel(hardware::Parcel *parcel) const; + + status_t writeEmbeddedToParcel( + hardware::Parcel *parcel, + size_t parentHandle, + size_t parentOffset) const; + +protected: + virtual ~JHwBlob(); + +private: + struct BlobInfo { + size_t mOffset; + sp<JHwBlob> mBlob; + }; + + jclass mClass; + jobject mObject; + + void *mBuffer; + size_t mSize; + bool mOwnsBuffer; + + size_t mHandle; + + Vector<BlobInfo> mSubBlobs; + + DISALLOW_COPY_AND_ASSIGN(JHwBlob); +}; + +int register_android_os_HwBlob(JNIEnv *env); + +} // namespace android + +#endif // ANDROID_OS_HW_BLOB_H + diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp new file mode 100644 index 000000000000..5c879b8894a1 --- /dev/null +++ b/core/jni/android_os_HwParcel.cpp @@ -0,0 +1,930 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "android_os_HwParcel" +#include <android-base/logging.h> + +#include "android_os_HwParcel.h" + +#include "android_os_HwBinder.h" +#include "android_os_HwBlob.h" +#include "android_os_HwRemoteBinder.h" + +#include <JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> +#include <hidl/Status.h> +#include <nativehelper/ScopedLocalRef.h> + +#include "core_jni_helpers.h" + +using android::AndroidRuntime; + +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; + +#define PACKAGE_PATH "android/os" +#define CLASS_NAME "HwParcel" +#define CLASS_PATH PACKAGE_PATH "/" CLASS_NAME + +namespace android { + +static struct fields_t { + jfieldID contextID; + jmethodID constructID; + +} gFields; + +void signalExceptionForError(JNIEnv *env, status_t err) { + switch (err) { + case OK: + break; + + case NO_MEMORY: + { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + break; + } + + case INVALID_OPERATION: + { + jniThrowException( + env, "java/lang/UnsupportedOperationException", NULL); + break; + } + + case BAD_VALUE: + { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + break; + } + + case -ERANGE: + case BAD_INDEX: + { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL); + break; + } + + case BAD_TYPE: + { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + break; + } + + case NAME_NOT_FOUND: + { + jniThrowException(env, "java/util/NoSuchElementException", NULL); + break; + } + + case PERMISSION_DENIED: + { + jniThrowException(env, "java/lang/SecurityException", NULL); + break; + } + + case NO_INIT: + { + jniThrowException( + env, "java/lang/RuntimeException", "Not initialized"); + break; + } + + case ALREADY_EXISTS: + { + jniThrowException( + env, "java/lang/RuntimeException", "Item already exists"); + break; + } + + default: + { + jniThrowException( + env, "java/lang/RuntimeException", "Unknown error"); + + break; + } + } +} + +// static +void JHwParcel::InitClass(JNIEnv *env) { + ScopedLocalRef<jclass> clazz( + env, FindClassOrDie(env, CLASS_PATH)); + + gFields.contextID = + GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J"); + + gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "(Z)V"); +} + +// static +sp<JHwParcel> JHwParcel::SetNativeContext( + JNIEnv *env, jobject thiz, const sp<JHwParcel> &context) { + sp<JHwParcel> old = (JHwParcel *)env->GetLongField(thiz, gFields.contextID); + + if (context != NULL) { + context->incStrong(NULL /* id */); + } + + if (old != NULL) { + old->decStrong(NULL /* id */); + } + + env->SetLongField(thiz, gFields.contextID, (long)context.get()); + + return old; +} + +// static +sp<JHwParcel> JHwParcel::GetNativeContext(JNIEnv *env, jobject thiz) { + return (JHwParcel *)env->GetLongField(thiz, gFields.contextID); +} + +JHwParcel::JHwParcel(JNIEnv *env, jobject thiz) + : mParcel(NULL), + mOwnsParcel(false), + mTransactCallback(nullptr), + mWasSent(false) { + jclass clazz = env->GetObjectClass(thiz); + CHECK(clazz != NULL); + + mClass = (jclass)env->NewGlobalRef(clazz); + mObject = env->NewWeakGlobalRef(thiz); +} + +JHwParcel::~JHwParcel() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + mStorage.release(env); + + setParcel(NULL, false /* assumeOwnership */); + + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + + env->DeleteGlobalRef(mClass); + mClass = NULL; +} + +hardware::Parcel *JHwParcel::getParcel() { + return mParcel; +} + +EphemeralStorage *JHwParcel::getStorage() { + return &mStorage; +} + +void JHwParcel::setParcel(hardware::Parcel *parcel, bool assumeOwnership) { + if (mParcel && mOwnsParcel) { + delete mParcel; + } + + mParcel = parcel; + mOwnsParcel = assumeOwnership; +} + +// static +jobject JHwParcel::NewObject(JNIEnv *env) { + ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH)); + + jmethodID constructID = + GetMethodIDOrDie(env, clazz.get(), "<init>", "(Z)V"); + + return env->NewObject(clazz.get(), constructID, false /* allocate */); +} + +void JHwParcel::setTransactCallback( + ::android::hardware::IBinder::TransactCallback cb) { + mTransactCallback = cb; +} + +void JHwParcel::send() { + CHECK(mTransactCallback != nullptr); + CHECK(mParcel != nullptr); + + mTransactCallback(*mParcel); + mTransactCallback = nullptr; + + mWasSent = true; +} + +bool JHwParcel::wasSent() const { + return mWasSent; +} + +} // namespace android + +//////////////////////////////////////////////////////////////////////////////// + +using namespace android; + +static void releaseNativeContext(void *nativeContext) { + sp<JHwParcel> parcel = (JHwParcel *)nativeContext; + + if (parcel != NULL) { + parcel->decStrong(NULL /* id */); + } +} + +static jlong JHwParcel_native_init(JNIEnv *env) { + JHwParcel::InitClass(env); + + return reinterpret_cast<jlong>(&releaseNativeContext); +} + +static void JHwParcel_native_setup( + JNIEnv *env, jobject thiz, jboolean allocate) { + sp<JHwParcel> context = new JHwParcel(env, thiz); + + if (allocate) { + context->setParcel(new hardware::Parcel, true /* assumeOwnership */); + } + + JHwParcel::SetNativeContext(env, thiz, context); +} + +static void JHwParcel_native_writeInterfaceToken( + JNIEnv *env, jobject thiz, jstring interfaceNameObj) { + if (interfaceNameObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL); + if (interfaceName) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + status_t err = parcel->writeInterfaceToken( + String16( + reinterpret_cast<const char16_t *>(interfaceName), + env->GetStringLength(interfaceNameObj))); + + env->ReleaseStringCritical(interfaceNameObj, interfaceName); + interfaceName = NULL; + + signalExceptionForError(env, err); + } +} + +static void JHwParcel_native_enforceInterface( + JNIEnv *env, jobject thiz, jstring interfaceNameObj) { + // XXX original binder Parcel enforceInterface implementation does some + // mysterious things regarding strictModePolicy(), figure out if we need + // that here as well. + if (interfaceNameObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL); + if (interfaceName) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + bool valid = parcel->enforceInterface( + String16( + reinterpret_cast<const char16_t *>(interfaceName), + env->GetStringLength(interfaceNameObj))); + + env->ReleaseStringCritical(interfaceNameObj, interfaceName); + interfaceName = NULL; + + if (!valid) { + jniThrowException( + env, + "java/lang/SecurityException", + "HWBinder invocation to an incorrect interface"); + } + } +} + +#define DEFINE_PARCEL_WRITER(Suffix,Type) \ +static void JHwParcel_native_write ## Suffix( \ + JNIEnv *env, jobject thiz, Type val) { \ + hardware::Parcel *parcel = \ + JHwParcel::GetNativeContext(env, thiz)->getParcel(); \ + \ + status_t err = parcel->write ## Suffix(val); \ + signalExceptionForError(env, err); \ +} + +#define DEFINE_PARCEL_READER(Suffix,Type) \ +static Type JHwParcel_native_read ## Suffix( \ + JNIEnv *env, jobject thiz) { \ + hardware::Parcel *parcel = \ + JHwParcel::GetNativeContext(env, thiz)->getParcel(); \ + \ + Type val; \ + status_t err = parcel->read ## Suffix(&val); \ + signalExceptionForError(env, err); \ + \ + return val; \ +} + +DEFINE_PARCEL_WRITER(Bool,jboolean) +DEFINE_PARCEL_WRITER(Int8,jbyte) +DEFINE_PARCEL_WRITER(Int16,jshort) +DEFINE_PARCEL_WRITER(Int32,jint) +DEFINE_PARCEL_WRITER(Int64,jlong) +DEFINE_PARCEL_WRITER(Float,jfloat) +DEFINE_PARCEL_WRITER(Double,jdouble) + +DEFINE_PARCEL_READER(Int8,jbyte) +DEFINE_PARCEL_READER(Int16,jshort) +DEFINE_PARCEL_READER(Int32,jint) +DEFINE_PARCEL_READER(Int64,jlong) +DEFINE_PARCEL_READER(Float,jfloat) +DEFINE_PARCEL_READER(Double,jdouble) + +static jboolean JHwParcel_native_readBool(JNIEnv *env, jobject thiz) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + bool val; + status_t err = parcel->readBool(&val); + signalExceptionForError(env, err); + + return (jboolean)val; +} + +static void JHwParcel_native_writeStatus( + JNIEnv *env, jobject thiz, jint statusCode) { + using hardware::Status; + + Status status; + switch (statusCode) { + case 0: // kStatusSuccess + status = Status::ok(); + break; + case -1: // kStatusError + status = Status::fromStatusT(UNKNOWN_ERROR); + break; + default: + CHECK(!"Should not be here"); + } + + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + status_t err = status.writeToParcel(parcel); + signalExceptionForError(env, err); +} + +static void JHwParcel_native_verifySuccess(JNIEnv *env, jobject thiz) { + using hardware::Status; + + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + Status status; + status_t err = status.readFromParcel(*parcel); + signalExceptionForError(env, err); +} + +static void JHwParcel_native_releaseTemporaryStorage( + JNIEnv *env, jobject thiz) { + JHwParcel::GetNativeContext(env, thiz)->getStorage()->release(env); +} + +static void JHwParcel_native_send(JNIEnv *env, jobject thiz) { + JHwParcel::GetNativeContext(env, thiz)->send(); +} + +static void JHwParcel_native_writeString( + JNIEnv *env, jobject thiz, jstring valObj) { + if (valObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz); + + const hidl_string *s = + impl->getStorage()->allocTemporaryString(env, valObj); + + hardware::Parcel *parcel = impl->getParcel(); + + size_t parentHandle; + status_t err = parcel->writeBuffer(s, sizeof(*s), &parentHandle); + + if (err == OK) { + err = s->writeEmbeddedToParcel( + parcel, parentHandle, 0 /* parentOffset */); + } + + signalExceptionForError(env, err); +} + +#define DEFINE_PARCEL_VECTOR_WRITER(Suffix,Type) \ +static void JHwParcel_native_write ## Suffix ## Vector( \ + JNIEnv *env, jobject thiz, Type ## Array valObj) { \ + if (valObj == NULL) { \ + jniThrowException(env, "java/lang/NullPointerException", NULL); \ + return; \ + } \ + \ + sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz); \ + \ + const hidl_vec<Type> *vec = \ + impl->getStorage()->allocTemporary ## Suffix ## Vector(env, valObj); \ + \ + hardware::Parcel *parcel = impl->getParcel(); \ + \ + size_t parentHandle; \ + status_t err = parcel->writeBuffer(vec, sizeof(*vec), &parentHandle); \ + \ + if (err == OK) { \ + size_t childHandle; \ + \ + err = vec->writeEmbeddedToParcel( \ + parcel, \ + parentHandle, \ + 0 /* parentOffset */, \ + &childHandle); \ + } \ + \ + signalExceptionForError(env, err); \ +} + +DEFINE_PARCEL_VECTOR_WRITER(Int8,jbyte) +DEFINE_PARCEL_VECTOR_WRITER(Int16,jshort) +DEFINE_PARCEL_VECTOR_WRITER(Int32,jint) +DEFINE_PARCEL_VECTOR_WRITER(Int64,jlong) +DEFINE_PARCEL_VECTOR_WRITER(Float,jfloat) +DEFINE_PARCEL_VECTOR_WRITER(Double,jdouble) + +static void JHwParcel_native_writeBoolVector( + JNIEnv *env, jobject thiz, jbooleanArray valObj) { + if (valObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz); + + void *vecPtr = + impl->getStorage()->allocTemporaryStorage(sizeof(hidl_vec<bool>)); + + hidl_vec<bool> *vec = new (vecPtr) hidl_vec<bool>; + + jsize len = env->GetArrayLength(valObj); + + jboolean *src = env->GetBooleanArrayElements(valObj, nullptr); + + bool *dst = + (bool *)impl->getStorage()->allocTemporaryStorage(len * sizeof(bool)); + + for (jsize i = 0; i < len; ++i) { + dst[i] = src[i]; + } + + env->ReleaseBooleanArrayElements(valObj, src, 0 /* mode */); + src = nullptr; + + vec->setToExternal(dst, len); + + hardware::Parcel *parcel = impl->getParcel(); + + size_t parentHandle; + status_t err = parcel->writeBuffer(vec, sizeof(*vec), &parentHandle); + + if (err == OK) { + size_t childHandle; + + err = vec->writeEmbeddedToParcel( + parcel, + parentHandle, + 0 /* parentOffset */, + &childHandle); + } + + signalExceptionForError(env, err); +} + +static void JHwParcel_native_writeStrongBinder( + JNIEnv *env, jobject thiz, jobject binderObj) { + sp<hardware::IBinder> binder; + if (binderObj != NULL) { + ScopedLocalRef<jclass> hwBinderKlass( + env, FindClassOrDie(env, PACKAGE_PATH "/HwBinder")); + + ScopedLocalRef<jclass> hwRemoteBinderKlass( + env, FindClassOrDie(env, PACKAGE_PATH "/HwRemoteBinder")); + + if (env->IsInstanceOf(binderObj, hwBinderKlass.get())) { + binder = JHwBinder::GetNativeContext(env, binderObj); + } else if (env->IsInstanceOf(binderObj, hwRemoteBinderKlass.get())) { + binder = JHwRemoteBinder::GetNativeContext( + env, binderObj)->getBinder(); + } else { + signalExceptionForError(env, INVALID_OPERATION); + return; + } + } + + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + status_t err = parcel->writeStrongBinder(binder); + signalExceptionForError(env, err); +} + +static jstring MakeStringObjFromHidlString(JNIEnv *env, const hidl_string &s) { + String16 utf16String(s.c_str(), s.size()); + + return env->NewString( + reinterpret_cast<const jchar *>(utf16String.string()), + utf16String.size()); +} + +static jstring JHwParcel_native_readString(JNIEnv *env, jobject thiz) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + size_t parentHandle; + + const hidl_string *s = static_cast<const hidl_string *>( + parcel->readBuffer(&parentHandle)); + + if (s == NULL) { + signalExceptionForError(env, UNKNOWN_ERROR); + return NULL; + } + + status_t err = const_cast<hidl_string *>(s)->readEmbeddedFromParcel( + *parcel, parentHandle, 0 /* parentOffset */); + + if (err != OK) { + signalExceptionForError(env, err); + return NULL; + } + + return MakeStringObjFromHidlString(env, *s); +} + +#define DEFINE_PARCEL_VECTOR_READER(Suffix,Type,NewType) \ +static Type ## Array JHwParcel_native_read ## Suffix ## Vector( \ + JNIEnv *env, jobject thiz) { \ + hardware::Parcel *parcel = \ + JHwParcel::GetNativeContext(env, thiz)->getParcel(); \ + \ + size_t parentHandle; \ + \ + const hidl_vec<Type> *vec = \ + (const hidl_vec<Type> *)parcel->readBuffer(&parentHandle); \ + \ + if (vec == NULL) { \ + signalExceptionForError(env, UNKNOWN_ERROR); \ + return NULL; \ + } \ + \ + size_t childHandle; \ + \ + status_t err = const_cast<hidl_vec<Type> *>(vec) \ + ->readEmbeddedFromParcel( \ + *parcel, \ + parentHandle, \ + 0 /* parentOffset */, \ + &childHandle); \ + \ + if (err != OK) { \ + signalExceptionForError(env, err); \ + return NULL; \ + } \ + \ + Type ## Array valObj = env->New ## NewType ## Array(vec->size()); \ + env->Set ## NewType ## ArrayRegion(valObj, 0, vec->size(), &(*vec)[0]); \ + \ + return valObj; \ +} + +DEFINE_PARCEL_VECTOR_READER(Int8,jbyte,Byte) +DEFINE_PARCEL_VECTOR_READER(Int16,jshort,Short) +DEFINE_PARCEL_VECTOR_READER(Int32,jint,Int) +DEFINE_PARCEL_VECTOR_READER(Int64,jlong,Long) +DEFINE_PARCEL_VECTOR_READER(Float,jfloat,Float) +DEFINE_PARCEL_VECTOR_READER(Double,jdouble,Double) + +static jbooleanArray JHwParcel_native_readBoolVector( + JNIEnv *env, jobject thiz) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + size_t parentHandle; + + const hidl_vec<bool> *vec = + (const hidl_vec<bool> *)parcel->readBuffer(&parentHandle); + + if (vec == NULL) { + signalExceptionForError(env, UNKNOWN_ERROR); + return NULL; + } + + size_t childHandle; + + status_t err = const_cast<hidl_vec<bool> *>(vec) + ->readEmbeddedFromParcel( + *parcel, + parentHandle, + 0 /* parentOffset */, + &childHandle); + + if (err != OK) { + signalExceptionForError(env, err); + return NULL; + } + + jbooleanArray valObj = env->NewBooleanArray(vec->size()); + + for (size_t i = 0; i < vec->size(); ++i) { + jboolean x = (*vec)[i]; + env->SetBooleanArrayRegion(valObj, i, 1, &x); + } + + return valObj; +} + +static jobjectArray MakeStringArray( + JNIEnv *env, const hidl_string *array, size_t size) { + ScopedLocalRef<jclass> stringKlass( + env, + env->FindClass("java/lang/String")); + + // XXX Why can't I use ScopedLocalRef<> for the arrayObj and the stringObjs? + + jobjectArray arrayObj = env->NewObjectArray(size, stringKlass.get(), NULL); + + for (size_t i = 0; i < size; ++i) { + jstring stringObj = MakeStringObjFromHidlString(env, array[i]); + + env->SetObjectArrayElement( + arrayObj, + i, + stringObj); + } + + return arrayObj; +} + +static jobjectArray JHwParcel_native_readStringVector( + JNIEnv *env, jobject thiz) { + typedef hidl_vec<hidl_string> string_vec; + + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + size_t parentHandle; + + const string_vec *vec= + (const string_vec *)parcel->readBuffer(&parentHandle); + + if (vec == NULL) { + signalExceptionForError(env, UNKNOWN_ERROR); + return NULL; + } + + size_t childHandle; + status_t err = const_cast<string_vec *>(vec)->readEmbeddedFromParcel( + *parcel, parentHandle, 0 /* parentOffset */, &childHandle); + + for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) { + err = const_cast<hidl_vec<hidl_string> *>(vec) + ->readEmbeddedFromParcel( + *parcel, + childHandle, + i * sizeof(hidl_string), + nullptr /* childHandle */); + } + + if (err != OK) { + signalExceptionForError(env, err); + return NULL; + } + + return MakeStringArray(env, &(*vec)[0], vec->size()); +} + +static void JHwParcel_native_writeStringVector( + JNIEnv *env, jobject thiz, jobjectArray arrayObj) { + typedef hidl_vec<hidl_string> string_vec; + + if (arrayObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + jsize len = env->GetArrayLength(arrayObj); + + sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz); + + void *vecPtr = + impl->getStorage()->allocTemporaryStorage(sizeof(string_vec)); + + string_vec *vec = new (vecPtr) string_vec; + + hidl_string *strings = impl->getStorage()->allocStringArray(len); + vec->setToExternal(strings, len); + + for (jsize i = 0; i < len; ++i) { + ScopedLocalRef<jstring> stringObj( + env, + (jstring)env->GetObjectArrayElement(arrayObj, i)); + + const hidl_string *s = + impl->getStorage()->allocTemporaryString(env, stringObj.get()); + + strings[i].setToExternal(s->c_str(), s->size()); + } + + hardware::Parcel *parcel = impl->getParcel(); + + size_t parentHandle; + status_t err = parcel->writeBuffer(vec, sizeof(*vec), &parentHandle); + + if (err == OK) { + size_t childHandle; + err = vec->writeEmbeddedToParcel( + parcel, + parentHandle, + 0 /* parentOffset */, + &childHandle); + + for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) { + err = (*vec)[i].writeEmbeddedToParcel( + parcel, + childHandle, + i * sizeof(hidl_string)); + } + } + + signalExceptionForError(env, err); +} + +static jobject JHwParcel_native_readStrongBinder(JNIEnv *env, jobject thiz) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + sp<hardware::IBinder> binder = parcel->readStrongBinder(); + + if (binder == NULL) { + return NULL; + } + + return JHwRemoteBinder::NewObject(env, binder); +} + +static jobject JHwParcel_native_readBuffer(JNIEnv *env, jobject thiz) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + size_t handle; + const void *ptr = parcel->readBuffer(&handle); + + if (ptr == nullptr) { + jniThrowException(env, "java/util/NoSuchElementException", NULL); + return nullptr; + } + + return JHwBlob::NewObject(env, ptr, handle); +} + +static jobject JHwParcel_native_readEmbeddedBuffer( + JNIEnv *env, jobject thiz, jlong parentHandle, jlong offset) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + size_t childHandle; + + const void *ptr = + parcel->readEmbeddedBuffer(&childHandle, parentHandle, offset); + + if (ptr == nullptr) { + jniThrowException(env, "java/util/NoSuchElementException", NULL); + return 0; + } + + return JHwBlob::NewObject(env, ptr, childHandle); +} + +static void JHwParcel_native_writeBuffer( + JNIEnv *env, jobject thiz, jobject blobObj) { + if (blobObj == nullptr) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, blobObj); + status_t err = blob->writeToParcel(parcel); + + if (err != OK) { + signalExceptionForError(env, err); + } +} + +static JNINativeMethod gMethods[] = { + { "native_init", "()J", (void *)JHwParcel_native_init }, + { "native_setup", "(Z)V", (void *)JHwParcel_native_setup }, + + { "writeInterfaceToken", "(Ljava/lang/String;)V", + (void *)JHwParcel_native_writeInterfaceToken }, + + { "writeBool", "(Z)V", (void *)JHwParcel_native_writeBool }, + { "writeInt8", "(B)V", (void *)JHwParcel_native_writeInt8 }, + { "writeInt16", "(S)V", (void *)JHwParcel_native_writeInt16 }, + { "writeInt32", "(I)V", (void *)JHwParcel_native_writeInt32 }, + { "writeInt64", "(J)V", (void *)JHwParcel_native_writeInt64 }, + { "writeFloat", "(F)V", (void *)JHwParcel_native_writeFloat }, + { "writeDouble", "(D)V", (void *)JHwParcel_native_writeDouble }, + + { "writeString", "(Ljava/lang/String;)V", + (void *)JHwParcel_native_writeString }, + + { "writeBoolVector", "([Z)V", (void *)JHwParcel_native_writeBoolVector }, + { "writeInt8Vector", "([B)V", (void *)JHwParcel_native_writeInt8Vector }, + { "writeInt16Vector", "([S)V", (void *)JHwParcel_native_writeInt16Vector }, + { "writeInt32Vector", "([I)V", (void *)JHwParcel_native_writeInt32Vector }, + { "writeInt64Vector", "([J)V", (void *)JHwParcel_native_writeInt64Vector }, + { "writeFloatVector", "([F)V", (void *)JHwParcel_native_writeFloatVector }, + + { "writeDoubleVector", "([D)V", + (void *)JHwParcel_native_writeDoubleVector }, + + { "writeStringVector", "([Ljava/lang/String;)V", + (void *)JHwParcel_native_writeStringVector }, + + { "writeStrongBinder", "(L" PACKAGE_PATH "/IHwBinder;)V", + (void *)JHwParcel_native_writeStrongBinder }, + + { "enforceInterface", "(Ljava/lang/String;)V", + (void *)JHwParcel_native_enforceInterface }, + + { "readBool", "()Z", (void *)JHwParcel_native_readBool }, + { "readInt8", "()B", (void *)JHwParcel_native_readInt8 }, + { "readInt16", "()S", (void *)JHwParcel_native_readInt16 }, + { "readInt32", "()I", (void *)JHwParcel_native_readInt32 }, + { "readInt64", "()J", (void *)JHwParcel_native_readInt64 }, + { "readFloat", "()F", (void *)JHwParcel_native_readFloat }, + { "readDouble", "()D", (void *)JHwParcel_native_readDouble }, + + { "readString", "()Ljava/lang/String;", + (void *)JHwParcel_native_readString }, + + { "readBoolVector", "()[Z", (void *)JHwParcel_native_readBoolVector }, + { "readInt8Vector", "()[B", (void *)JHwParcel_native_readInt8Vector }, + { "readInt16Vector", "()[S", (void *)JHwParcel_native_readInt16Vector }, + { "readInt32Vector", "()[I", (void *)JHwParcel_native_readInt32Vector }, + { "readInt64Vector", "()[J", (void *)JHwParcel_native_readInt64Vector }, + { "readFloatVector", "()[F", (void *)JHwParcel_native_readFloatVector }, + { "readDoubleVector", "()[D", (void *)JHwParcel_native_readDoubleVector }, + + { "readStringVector", "()[Ljava/lang/String;", + (void *)JHwParcel_native_readStringVector }, + + { "readStrongBinder", "()L" PACKAGE_PATH "/IHwBinder;", + (void *)JHwParcel_native_readStrongBinder }, + + { "writeStatus", "(I)V", (void *)JHwParcel_native_writeStatus }, + + { "verifySuccess", "()V", (void *)JHwParcel_native_verifySuccess }, + + { "releaseTemporaryStorage", "()V", + (void *)JHwParcel_native_releaseTemporaryStorage }, + + { "send", "()V", (void *)JHwParcel_native_send }, + + { "readBuffer", "()L" PACKAGE_PATH "/HwBlob;", + (void *)JHwParcel_native_readBuffer }, + + { "readEmbeddedBuffer", "(JJ)L" PACKAGE_PATH "/HwBlob;", + (void *)JHwParcel_native_readEmbeddedBuffer }, + + { "writeBuffer", "(L" PACKAGE_PATH "/HwBlob;)V", + (void *)JHwParcel_native_writeBuffer }, +}; + +namespace android { + +int register_android_os_HwParcel(JNIEnv *env) { + return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods)); +} + +} // namespace android diff --git a/core/jni/android_os_HwParcel.h b/core/jni/android_os_HwParcel.h new file mode 100644 index 000000000000..708bbba1901c --- /dev/null +++ b/core/jni/android_os_HwParcel.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_OS_HW_PARCEL_H +#define ANDROID_OS_HW_PARCEL_H + +#include "hwbinder/EphemeralStorage.h" + +#include <android-base/macros.h> +#include <hwbinder/IBinder.h> +#include <hwbinder/Parcel.h> +#include <jni.h> +#include <utils/RefBase.h> + +namespace android { + +struct JHwParcel : public RefBase { + static void InitClass(JNIEnv *env); + + static sp<JHwParcel> SetNativeContext( + JNIEnv *env, jobject thiz, const sp<JHwParcel> &context); + + static sp<JHwParcel> GetNativeContext(JNIEnv *env, jobject thiz); + + static jobject NewObject(JNIEnv *env); + + JHwParcel(JNIEnv *env, jobject thiz); + + void setParcel(hardware::Parcel *parcel, bool assumeOwnership); + hardware::Parcel *getParcel(); + + EphemeralStorage *getStorage(); + + void setTransactCallback(::android::hardware::IBinder::TransactCallback cb); + + void send(); + bool wasSent() const; + +protected: + virtual ~JHwParcel(); + +private: + jclass mClass; + jobject mObject; + + hardware::Parcel *mParcel; + bool mOwnsParcel; + + EphemeralStorage mStorage; + + ::android::hardware::IBinder::TransactCallback mTransactCallback; + bool mWasSent; + + DISALLOW_COPY_AND_ASSIGN(JHwParcel); +}; + +void signalExceptionForError(JNIEnv *env, status_t err); +int register_android_os_HwParcel(JNIEnv *env); + +} // namespace android + +#endif // ANDROID_OS_HW_PARCEL_H diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp new file mode 100644 index 000000000000..3023ba87d24b --- /dev/null +++ b/core/jni/android_os_HwRemoteBinder.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "JHwRemoteBinder" +#include <android-base/logging.h> + +#include "android_os_HwRemoteBinder.h" + +#include "android_os_HwParcel.h" + +#include <JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> +#include <hidl/IServiceManager.h> +#include <hidl/Status.h> +#include <nativehelper/ScopedLocalRef.h> + +#include "core_jni_helpers.h" + +using android::AndroidRuntime; + +#define PACKAGE_PATH "android/os" +#define CLASS_NAME "HwRemoteBinder" +#define CLASS_PATH PACKAGE_PATH "/" CLASS_NAME + +namespace android { + +static struct fields_t { + jfieldID contextID; + jmethodID constructID; + +} gFields; + +// static +void JHwRemoteBinder::InitClass(JNIEnv *env) { + ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH)); + + gFields.contextID = + GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J"); + + gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V"); +} + +// static +sp<JHwRemoteBinder> JHwRemoteBinder::SetNativeContext( + JNIEnv *env, jobject thiz, const sp<JHwRemoteBinder> &context) { + sp<JHwRemoteBinder> old = + (JHwRemoteBinder *)env->GetLongField(thiz, gFields.contextID); + + if (context != NULL) { + context->incStrong(NULL /* id */); + } + + if (old != NULL) { + old->decStrong(NULL /* id */); + } + + env->SetLongField(thiz, gFields.contextID, (long)context.get()); + + return old; +} + +// static +sp<JHwRemoteBinder> JHwRemoteBinder::GetNativeContext( + JNIEnv *env, jobject thiz) { + return (JHwRemoteBinder *)env->GetLongField(thiz, gFields.contextID); +} + +// static +jobject JHwRemoteBinder::NewObject( + JNIEnv *env, const sp<hardware::IBinder> &binder) { + ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH)); + + // XXX Have to look up the constructor here because otherwise that static + // class initializer isn't called and gFields.constructID is undefined :( + + jmethodID constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V"); + + jobject obj = env->NewObject(clazz.get(), constructID); + JHwRemoteBinder::GetNativeContext(env, obj)->setBinder(binder); + + return obj; +} + +JHwRemoteBinder::JHwRemoteBinder( + JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder) + : mBinder(binder) { + jclass clazz = env->GetObjectClass(thiz); + CHECK(clazz != NULL); + + mClass = (jclass)env->NewGlobalRef(clazz); + mObject = env->NewWeakGlobalRef(thiz); +} + +JHwRemoteBinder::~JHwRemoteBinder() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + + env->DeleteGlobalRef(mClass); + mClass = NULL; +} + +sp<hardware::IBinder> JHwRemoteBinder::getBinder() { + return mBinder; +} + +void JHwRemoteBinder::setBinder(const sp<hardware::IBinder> &binder) { + mBinder = binder; +} + +} // namespace android + +//////////////////////////////////////////////////////////////////////////////// + +using namespace android; + +static void releaseNativeContext(void *nativeContext) { + sp<JHwRemoteBinder> binder = (JHwRemoteBinder *)nativeContext; + + if (binder != NULL) { + binder->decStrong(NULL /* id */); + } +} + +static jlong JHwRemoteBinder_native_init(JNIEnv *env) { + JHwRemoteBinder::InitClass(env); + + return reinterpret_cast<jlong>(&releaseNativeContext); +} + +static void JHwRemoteBinder_native_setup_empty(JNIEnv *env, jobject thiz) { + sp<JHwRemoteBinder> context = + new JHwRemoteBinder(env, thiz, NULL /* service */); + + JHwRemoteBinder::SetNativeContext(env, thiz, context); +} + +static void JHwRemoteBinder_native_transact( + JNIEnv *env, + jobject thiz, + jint code, + jobject requestObj, + jobject replyObj, + jint flags) { + sp<hardware::IBinder> binder = + JHwRemoteBinder::GetNativeContext(env, thiz)->getBinder(); + + if (requestObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + const hardware::Parcel *request = + JHwParcel::GetNativeContext(env, requestObj)->getParcel(); + + hardware::Parcel *reply = + JHwParcel::GetNativeContext(env, replyObj)->getParcel(); + + status_t err = binder->transact(code, *request, reply, flags); + signalExceptionForError(env, err); +} + +static JNINativeMethod gMethods[] = { + { "native_init", "()J", (void *)JHwRemoteBinder_native_init }, + + { "native_setup_empty", "()V", + (void *)JHwRemoteBinder_native_setup_empty }, + + { "transact", + "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V", + (void *)JHwRemoteBinder_native_transact }, +}; + +namespace android { + +int register_android_os_HwRemoteBinder(JNIEnv *env) { + return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods)); +} + +} // namespace android + diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h new file mode 100644 index 000000000000..fd33338986a0 --- /dev/null +++ b/core/jni/android_os_HwRemoteBinder.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_OS_HW_REMOTE_BINDER_H +#define ANDROID_OS_HW_REMOTE_BINDER_H + +#include <android-base/macros.h> +#include <hwbinder/Binder.h> +#include <jni.h> +#include <utils/RefBase.h> + +namespace android { + +struct JHwRemoteBinder : public RefBase { + static void InitClass(JNIEnv *env); + + static sp<JHwRemoteBinder> SetNativeContext( + JNIEnv *env, jobject thiz, const sp<JHwRemoteBinder> &context); + + static sp<JHwRemoteBinder> GetNativeContext(JNIEnv *env, jobject thiz); + + static jobject NewObject(JNIEnv *env, const sp<hardware::IBinder> &binder); + + JHwRemoteBinder( + JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder); + + sp<hardware::IBinder> getBinder(); + void setBinder(const sp<hardware::IBinder> &binder); + +protected: + virtual ~JHwRemoteBinder(); + +private: + jclass mClass; + jobject mObject; + + sp<hardware::IBinder> mBinder; + + DISALLOW_COPY_AND_ASSIGN(JHwRemoteBinder); +}; + +int register_android_os_HwRemoteBinder(JNIEnv *env); + +} // namespace android + +#endif // ANDROID_OS_HW_REMOTE_BINDER_H + diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp index 8ba77aed1d04..4b68c0dad038 100644 --- a/core/jni/android_os_SELinux.cpp +++ b/core/jni/android_os_SELinux.cpp @@ -23,9 +23,9 @@ #include "selinux/selinux.h" #include "selinux/android.h" #include <errno.h> +#include <memory> #include <ScopedLocalRef.h> #include <ScopedUtfChars.h> -#include <UniquePtr.h> namespace android { @@ -34,7 +34,7 @@ struct SecurityContext_Delete { freecon(p); } }; -typedef UniquePtr<char[], SecurityContext_Delete> Unique_SecurityContext; +typedef std::unique_ptr<char[], SecurityContext_Delete> Unique_SecurityContext; static jboolean isSELinuxDisabled = true; @@ -112,7 +112,7 @@ static jboolean setFSCreateCon(JNIEnv *env, jobject, jstring contextStr) { return false; } - UniquePtr<ScopedUtfChars> context; + std::unique_ptr<ScopedUtfChars> context; const char* context_c_str = NULL; if (contextStr != NULL) { context.reset(new ScopedUtfChars(env, contextStr)); diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index 364ac44ee0f6..7e016576d70a 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -20,7 +20,6 @@ #include "core_jni_helpers.h" #include <ScopedUtfChars.h> -#include <UniquePtr.h> #include <androidfw/ZipFileRO.h> #include <androidfw/ZipUtils.h> #include <utils/Log.h> @@ -37,6 +36,7 @@ #include <sys/stat.h> #include <sys/types.h> +#include <memory> #define APK_LIB "lib/" #define APK_LIB_LEN (sizeof(APK_LIB) - 1) @@ -398,7 +398,7 @@ iterateOverNativeFiles(JNIEnv *env, jlong apkHandle, jstring javaCpuAbi, return INSTALL_FAILED_INVALID_APK; } - UniquePtr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile)); + std::unique_ptr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile)); if (it.get() == NULL) { return INSTALL_FAILED_INVALID_APK; } @@ -446,7 +446,7 @@ static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supported return INSTALL_FAILED_INVALID_APK; } - UniquePtr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile)); + std::unique_ptr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile)); if (it.get() == NULL) { return INSTALL_FAILED_INVALID_APK; } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 3f4b2a61321b..5202a98bc030 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -32,6 +32,7 @@ #include <signal.h> #include <stdlib.h> #include <sys/capability.h> +#include <sys/cdefs.h> #include <sys/personality.h> #include <sys/prctl.h> #include <sys/resource.h> @@ -667,11 +668,39 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* env, jclass) { // Zygote process unmount root storage space initially before every child processes are forked. // Every forked child processes (include SystemServer) only mount their own root storage space - // And no need unmount storage operation in MountEmulatedStorage method. - // Zygote process does not utilize root storage spaces and unshared its mount namespace from the ART. + // and no need unmount storage operation in MountEmulatedStorage method. + // Zygote process does not utilize root storage spaces and unshares its mount namespace below. + + // See storage config details at http://source.android.com/tech/storage/ + // Create private mount namespace shared by all children + if (unshare(CLONE_NEWNS) == -1) { + RuntimeAbort(env, __LINE__, "Failed to unshare()"); + return; + } + + // Mark rootfs as being a slave so that changes from default + // namespace only flow into our children. + if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) { + RuntimeAbort(env, __LINE__, "Failed to mount() rootfs as MS_SLAVE"); + return; + } + + // Create a staging tmpfs that is shared by our children; they will + // bind mount storage into their respective private namespaces, which + // are isolated from each other. + const char* target_base = getenv("EMULATED_STORAGE_TARGET"); + if (target_base != nullptr) { +#define STRINGIFY_UID(x) __STRING(x) + if (mount("tmpfs", target_base, "tmpfs", MS_NOSUID | MS_NODEV, + "uid=0,gid=" STRINGIFY_UID(AID_SDCARD_R) ",mode=0751") == -1) { + ALOGE("Failed to mount tmpfs to %s", target_base); + RuntimeAbort(env, __LINE__, "Failed to mount tmpfs"); + return; + } +#undef STRINGIFY_UID + } UnmountTree("/storage"); - return; } static const JNINativeMethod gMethods[] = { diff --git a/core/jni/hwbinder/EphemeralStorage.cpp b/core/jni/hwbinder/EphemeralStorage.cpp new file mode 100644 index 000000000000..4996bc86cade --- /dev/null +++ b/core/jni/hwbinder/EphemeralStorage.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "EphemeralStorage" +//#define LOG_NDEBUG 0 + +#include <android-base/logging.h> + +#include "EphemeralStorage.h" + +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; + +namespace android { + +EphemeralStorage::EphemeralStorage() { +} + +EphemeralStorage::~EphemeralStorage() { + CHECK(mItems.empty()) + << "All item storage should have been released by now."; +} + +hidl_string *EphemeralStorage::allocStringArray(size_t size) { + Item item; + item.mType = TYPE_STRING_ARRAY; + item.mObj = NULL; + item.mPtr = new hidl_string[size]; + mItems.push_back(item); + + return static_cast<hidl_string *>(item.mPtr); +} + +void *EphemeralStorage::allocTemporaryStorage(size_t size) { + Item item; + item.mType = TYPE_STORAGE; + item.mObj = NULL; + item.mPtr = malloc(size); + mItems.push_back(item); + + return item.mPtr; +} + +const hidl_string *EphemeralStorage::allocTemporaryString( + JNIEnv *env, jstring stringObj) { + jstring obj = (jstring)env->NewGlobalRef(stringObj); + const char *val = env->GetStringUTFChars(obj, NULL); + + Item item; + item.mType = TYPE_STRING; + item.mObj = obj; + item.mPtr = (void *)val; + mItems.push_back(item); + + hidl_string *s = allocStringArray(1 /* size */); + s->setToExternal((char *)val, strlen(val)); + + return s; +} + +#define DEFINE_ALLOC_VECTOR_METHODS(Suffix,Type,NewType) \ +const hidl_vec<Type> *EphemeralStorage::allocTemporary ## Suffix ## Vector( \ + JNIEnv *env, Type ## Array arrayObj) { \ + Type ## Array obj = (Type ## Array)env->NewGlobalRef(arrayObj); \ + jsize len = env->GetArrayLength(obj); \ + const Type *val = env->Get ## NewType ## ArrayElements(obj, NULL); \ + \ + Item item; \ + item.mType = TYPE_ ## Suffix ## _ARRAY; \ + item.mObj = obj; \ + item.mPtr = (void *)val; \ + mItems.push_back(item); \ + \ + void *vecPtr = allocTemporaryStorage(sizeof(hidl_vec<Type>)); \ + \ + hidl_vec<Type> *vec = new (vecPtr) hidl_vec<Type>; \ + vec->setToExternal(const_cast<Type *>(val), len); \ + \ + return vec; \ +} + +DEFINE_ALLOC_VECTOR_METHODS(Int8,jbyte,Byte) +DEFINE_ALLOC_VECTOR_METHODS(Int16,jshort,Short) +DEFINE_ALLOC_VECTOR_METHODS(Int32,jint,Int) +DEFINE_ALLOC_VECTOR_METHODS(Int64,jlong,Long) +DEFINE_ALLOC_VECTOR_METHODS(Float,jfloat,Float) +DEFINE_ALLOC_VECTOR_METHODS(Double,jdouble,Double) + +#define DEFINE_RELEASE_ARRAY_CASE(Suffix,Type,NewType) \ + case TYPE_ ## Suffix ## _ARRAY: \ + { \ + env->Release ## NewType ## ArrayElements( \ + (Type ## Array)item.mObj, \ + (Type *)item.mPtr, \ + 0 /* mode */); \ + \ + env->DeleteGlobalRef(item.mObj); \ + break; \ + } + +void EphemeralStorage::release(JNIEnv *env) { + for (size_t i = mItems.size(); i--;) { + const Item &item = mItems[i]; + + switch (item.mType) { + case TYPE_STRING_ARRAY: + { + delete[] static_cast<hidl_string *>(item.mPtr); + break; + } + + case TYPE_STORAGE: + { + free(item.mPtr); + break; + } + + case TYPE_STRING: + { + env->ReleaseStringUTFChars( + (jstring)item.mObj, (const char *)item.mPtr); + + env->DeleteGlobalRef(item.mObj); + break; + } + + DEFINE_RELEASE_ARRAY_CASE(Int8,jbyte,Byte) + DEFINE_RELEASE_ARRAY_CASE(Int16,jshort,Short) + DEFINE_RELEASE_ARRAY_CASE(Int32,jint,Int) + DEFINE_RELEASE_ARRAY_CASE(Int64,jlong,Long) + DEFINE_RELEASE_ARRAY_CASE(Float,jfloat,Float) + DEFINE_RELEASE_ARRAY_CASE(Double,jdouble,Double) + + default: + CHECK(!"Should not be here"); + } + } + + mItems.clear(); +} + +} // namespace android + diff --git a/core/jni/hwbinder/EphemeralStorage.h b/core/jni/hwbinder/EphemeralStorage.h new file mode 100644 index 000000000000..f07c782bfdf7 --- /dev/null +++ b/core/jni/hwbinder/EphemeralStorage.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EPHEMERAL_STORAGE_H_ + +#define EPHEMERAL_STORAGE_H_ + +#include <android-base/macros.h> +#include <hidl/HidlSupport.h> +#include <jni.h> +#include <utils/Vector.h> + +namespace android { + +#define DECLARE_ALLOC_METHODS(Suffix,Type) \ + const ::android::hardware::hidl_vec<Type> * \ + allocTemporary ## Suffix ## Vector( \ + JNIEnv *env, Type ## Array arrayObj); + +struct EphemeralStorage { + EphemeralStorage(); + ~EphemeralStorage(); + + void release(JNIEnv *env); + + hardware::hidl_string *allocStringArray(size_t size); + + void *allocTemporaryStorage(size_t size); + + const ::android::hardware::hidl_string *allocTemporaryString( + JNIEnv *env, jstring stringObj); + + DECLARE_ALLOC_METHODS(Int8,jbyte) + DECLARE_ALLOC_METHODS(Int16,jshort) + DECLARE_ALLOC_METHODS(Int32,jint) + DECLARE_ALLOC_METHODS(Int64,jlong) + DECLARE_ALLOC_METHODS(Float,jfloat) + DECLARE_ALLOC_METHODS(Double,jdouble) + +private: + enum Type { + TYPE_STRING_ARRAY, + TYPE_STORAGE, + TYPE_STRING, + TYPE_Int8_ARRAY, + TYPE_Int16_ARRAY, + TYPE_Int32_ARRAY, + TYPE_Int64_ARRAY, + TYPE_Float_ARRAY, + TYPE_Double_ARRAY, + }; + + struct Item { + Type mType; + jobject mObj; + void *mPtr; + }; + + Vector<Item> mItems; + + DISALLOW_COPY_AND_ASSIGN(EphemeralStorage); +}; + +#undef DECLARE_ALLOC_METHODS + +} // namespace android + +#endif // EPHEMERAL_STORAGE_H_ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index f712c346f88a..6c5182459d04 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -198,6 +198,7 @@ <protected-broadcast android:name="android.btopp.intent.action.OPEN" /> <protected-broadcast android:name="android.btopp.intent.action.OPEN_INBOUND" /> <protected-broadcast android:name="android.btopp.intent.action.TRANSFER_COMPLETE" /> + <protected-broadcast android:name="com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN" /> <protected-broadcast android:name="com.android.bluetooth.pbap.authchall" /> <protected-broadcast android:name="com.android.bluetooth.pbap.userconfirmtimeout" /> <protected-broadcast android:name="com.android.bluetooth.pbap.authresponse" /> diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk new file mode 100644 index 000000000000..a6f87ea2e5df --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk @@ -0,0 +1,38 @@ +# Copyright (C) 2014 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_SDK_VERSION := 18 + +LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp_corrupted + +LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex + +LOCAL_DEX_PREOPT := false + +include $(BUILD_PACKAGE) + +corrupted_classes2_dex := $(dir $(built_dex))/classes2.dex + +$(corrupted_classes2_dex): $(built_dex) + $(hide) touch $@ + +$(LOCAL_BUILT_MODULE): $(corrupted_classes2_dex) diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml new file mode 100644 index 000000000000..7993c6f48f83 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.framework.multidexlegacycorrupteddex" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="18" + android:targetSdkVersion="18" /> + + <application + android:name="android.support.multidex.MultiDexApplication" + android:allowBackup="true" + android:label="MultiDexLegacyTestApp_corrupted"> + <activity + android:name="com.android.framework.multidexlegacycorrupteddex.MainActivity" + android:label="MultiDexLegacyTestApp_corrupted" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.framework.multidexlegacycorrupteddex" + android:label="Test for MultiDexLegacyTestApp_corrupted" /> + +</manifest> diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/res/layout/activity_main.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/res/layout/activity_main.xml new file mode 100644 index 000000000000..58ae67a6d11e --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/res/layout/activity_main.xml @@ -0,0 +1,7 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".MainActivity" > + +</RelativeLayout> diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/CorruptedDexTest.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/CorruptedDexTest.java new file mode 100644 index 000000000000..afef394344cb --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/CorruptedDexTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 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.framework.multidexlegacycorrupteddex; + +import android.test.ActivityInstrumentationTestCase2; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +/** + * Run the tests with: <code>adb shell am instrument -w + com.android.framework.multidexlegacycorrupteddex/android.test.InstrumentationTestRunner +</code> + */ +public class CorruptedDexTest extends ActivityInstrumentationTestCase2<MainActivity> +{ + + public CorruptedDexTest() { + super(MainActivity.class); + } + + /** + * Tests that when a {@link ClassNotFoundException} is thrown, the message also contains + * something about the suppressed IOException. + */ + public void testSupressedExceptions() + { + try { + Class.forName("notapackage.NotAClass"); + throw new AssertionError(); + } catch (ClassNotFoundException e) { + // expected + +// This the check we should do but API is not yet available in 19 +// Throwable[] suppressed = e.getSuppressed(); +// assertTrue(suppressed.length > 0); +// boolean ioFound = false; +// for (Throwable throwable : suppressed) { +// if (throwable instanceof IOException) { +// ioFound = true; +// break; +// } +// } +// assertTrue(ioFound); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(baos); + e.printStackTrace(ps); + ps.close(); + assertTrue("Exception message should mention IOException but is not: " + + baos.toString(), + baos.toString().contains("IOException")); + } + } +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/MainActivity.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/MainActivity.java new file mode 100644 index 000000000000..61ba5b866499 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/MainActivity.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 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.framework.multidexlegacycorrupteddex; + +import android.app.Activity; +import android.os.Bundle; + +public class MainActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + } + +} diff --git a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java index da8bc1d0945d..7935880d12a9 100644 --- a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java @@ -45,6 +45,7 @@ public class WakeupMessageTest { private static final int TEST_CMD = 18; private static final int TEST_ARG1 = 33; private static final int TEST_ARG2 = 182; + private static final Object TEST_OBJ = "hello"; @Mock AlarmManager mAlarmManager; WakeupMessage mMessage; @@ -92,7 +93,7 @@ public class WakeupMessageTest { mListenerCaptor.capture(), any(Handler.class)); mMessage = new WakeupMessage(context, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1, - TEST_ARG2); + TEST_ARG2, TEST_OBJ); } /** @@ -114,6 +115,7 @@ public class WakeupMessageTest { assertEquals("what", TEST_CMD, mHandler.getLastMessage().what); assertEquals("arg1", TEST_ARG1, mHandler.getLastMessage().arg1); assertEquals("arg2", TEST_ARG2, mHandler.getLastMessage().arg2); + assertEquals("obj", TEST_OBJ, mHandler.getLastMessage().obj); } /** diff --git a/docs/html/guide/topics/renderscript/compute.jd b/docs/html/guide/topics/renderscript/compute.jd index c5b49d70435f..b30c74104bc1 100755 --- a/docs/html/guide/topics/renderscript/compute.jd +++ b/docs/html/guide/topics/renderscript/compute.jd @@ -10,12 +10,13 @@ parent.link=index.html <ol> <li><a href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a></li> - <li><a href="#access-rs-apis">Accessing RenderScript APIs</a> + <li><a href="#access-rs-apis">Accessing RenderScript APIs from Java</a> <ol> <li><a href="#ide-setup">Setting Up Your Development Environment</a></li> </ol> </li> <li><a href="#using-rs-from-java">Using RenderScript from Java Code</a></li> + <li><a href="#single-source-rs">Single-Source RenderScript</a></li> <li><a href="#reduction-in-depth">Reduction Kernels in Depth</a> <ol> <li><a href="#writing-reduction-kernel">Writing a reduction kernel</a></li> @@ -45,12 +46,16 @@ computer vision.</p> <p>To begin with RenderScript, there are two main concepts you should understand:</p> <ul> -<li>High-performance compute kernels are written in a C99-derived language. A <i>compute - kernel</i> is a function or collection of functions that you can direct the RenderScript runtime - to execute in parallel across a collection of data.</li> +<li>The <em>language</em> itself is a C99-derived language for writing high-performance compute +code. <a href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a> describes +how to use it to write compute kernels.</li> -<li>A Java API is used for managing the lifetime of RenderScript resources and controlling kernel -execution.</li> +<li>The <em>control API</em> is used for managing the lifetime of RenderScript resources and +controlling kernel execution. It is available in three different languages: Java, C++ in Android +NDK, and the C99-derived kernel language itself. +<a href="#using-rs-from-java">Using RenderScript from Java Code</a> and +<a href=#single-source-rs>Single-Source RenderScript</a> describe the first and the third +options, respectively.</li> </ul> <h2 id="writing-an-rs-kernel">Writing a RenderScript Kernel</h2> @@ -77,7 +82,9 @@ initial setup or serial computations within a larger processing pipeline.</li> access script globals from Java code, and these are often used for parameter passing to RenderScript kernels.</p></li> -<li><p>Zero or more <strong><i>compute kernels</i></strong>. There are two kinds of compute +<li><p>Zero or more <strong><i>compute kernels</i></strong>. A compute kernel is a function +or collection of functions that you can direct the RenderScript runtime to execute in parallel +across a collection of data. There are two kinds of compute kernels: <i>mapping</i> kernels (also called <i>foreach</i> kernels) and <i>reduction</i> kernels.</p> @@ -243,9 +250,9 @@ beneficial on some architectures due to additional optimizations only available precision (such as SIMD CPU instructions).</p> -<h2 id="access-rs-apis">Accessing RenderScript APIs</h2> +<h2 id="access-rs-apis">Accessing RenderScript APIs from Java</h2> -<p>When developing an Android application that uses RenderScript, you can access its API in +<p>When developing an Android application that uses RenderScript, you can access its API from Java in one of two ways:</p> <ul> @@ -377,7 +384,7 @@ to you when using RenderScript: <ul> <li><strong>ScriptC</strong>: These are the user-defined scripts as described in <a -href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a> above. Every script has a Java class +href="#writing-an-rs-kernel"><i>Writing a RenderScript Kernel</i></a> above. Every script has a Java class reflected by the RenderScript compiler in order to make it easy to access the script from Java code; this class has the name <code>ScriptC_<i>filename</i></code>. For example, if the mapping kernel above were located in <code>invert.rs</code> and a RenderScript context were already located in @@ -448,6 +455,116 @@ context to throw an exception.</li> </ol> a <code>get()</code> method to obtain the result of a reduction. <code>get()</code> is synchronous, and is serialized with respect to the reduction (which is asynchronous).</p> +<h2 id="single-source-rs">Single-Source RenderScript</h2> + +<p>Android 7.0 (API level 24) introduces a new programming feature called <em>Single-Source +RenderScript</em>, in which kernels are launched from the script where they are defined, rather than +from Java. This approach is currently limited to mapping kernels, which are simply referred to as "kernels" +in this section for conciseness. This new feature also supports creating allocations of type +<a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_allocation> +<code>rs_allocation</code></a> from inside the script. It is now possible to +implement a whole algorithm solely within a script, even if multiple kernel launches are required. +The benefit is twofold: more readable code, because it keeps the implementation of an algorithm in +one language; and potentially faster code, because of fewer transitions between Java and +RenderScript across multiple kernel launches.</p> + +<p>In Single-Source RenderScript, you write kernels as described in <a href="#writing-an-rs-kernel"> +Writing a RenderScript Kernel</a>. You then write an invokable function that calls +<a href="{@docRoot}guide/topics/renderscript/reference/rs_for_each.html#android_rs:rsForEach"> +<code>rsForEach()</code></a> to launch them. That API takes a kernel function as the first +parameter, followed by input and output allocations. A similar API +<a href="{@docRoot}guide/topics/renderscript/reference/rs_for_each.html#android_rs:rsForEachWithOptions"> +<code>rsForEachWithOptions()</code></a> takes an extra argument of type +<a href="{@docRoot}guide/topics/renderscript/reference/rs_for_each.html#android_rs:rs_script_call_t"> +<code>rs_script_call_t</code></a>, which specifies a subset of the elements from the input and +output allocations for the kernel function to process.</p> + +<p>To start RenderScript computation, you call the invokable function from Java. +Follow the steps in <a href="#using-rs-from-java">Using RenderScript from Java Code</a>. +In the step <a href="#launching_kernels">launch the appropriate kernels</a>, call +the invokable function using <code>invoke_<i>function_name</i>()</code>, which will start the +whole computation, including launching kernels.</p> + +<p>Allocations are often needed to save and pass +intermediate results from one kernel launch to another. You can create them using +<a href="{@docRoot}guide/topics/renderscript/reference/rs_allocation_create.html#android_rs:rsCreateAllocation"> +rsCreateAllocation()</a>. One easy-to-use form of that API is <code> +rsCreateAllocation_<T><W>(…)</code>, where <i>T</i> is the data type for an +element, and <i>W</i> is the vector width for the element. The API takes the sizes in +dimensions X, Y, and Z as arguments. For 1D or 2D allocations, the size for dimension Y or Z can +be omitted. For example, <code>rsCreateAllocation_uchar4(16384)</code> creates a 1D allocation of +16384 elements, each of which is of type <code>uchar4</code>.</p> + +<p>Allocations are managed by the system automatically. You +do not have to explicitly release or free them. However, you can call +<a href="{@docRoot}guide/topics/renderscript/reference/rs_object_info.html#android_rs:rsClearObject"> +<code>rsClearObject(rs_allocation* alloc)</code></a> to indicate you no longer need the handle +<code>alloc</code> to the underlying allocation, +so that the system can free up resources as early as possible.</p> + +<p>The <a href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a> section contains an example +kernel that inverts an image. The example below expands that to apply more than one effect to an image, +using Single-Source RenderScript. It includes another kernel, <code>greyscale</code>, which turns a +color image into black-and-white. An invokable function <code>process()</code> then applies those two kernels +consecutively to an input image, and produces an output image. Allocations for both the input and +the output are passed in as arguments of type +<a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_allocation> +<code>rs_allocation</code></a>.</p> + +<pre> +// File: singlesource.rs + +#pragma version(1) +#pragma rs java_package_name(com.android.rssample) + +static const float4 weight = {0.299f, 0.587f, 0.114f, 0.0f}; + +uchar4 RS_KERNEL invert(uchar4 in, uint32_t x, uint32_t y) { + uchar4 out = in; + out.r = 255 - in.r; + out.g = 255 - in.g; + out.b = 255 - in.b; + return out; +} + +uchar4 RS_KERNEL greyscale(uchar4 in) { + const float4 inF = rsUnpackColor8888(in); + const float4 outF = (float4){ dot(inF, weight) }; + return rsPackColorTo8888(outF); +} + +void process(rs_allocation inputImage, rs_allocation outputImage) { + const uint32_t imageWidth = rsAllocationGetDimX(inputImage); + const uint32_t imageHeight = rsAllocationGetDimY(inputImage); + rs_allocation tmp = rsCreateAllocation_uchar4(imageWidth, imageHeight); + rsForEach(invert, inputImage, tmp); + rsForEach(greyscale, tmp, outputImage); +} +</pre> + +<p>You can call the <code>process()</code> function from Java as follows:</p> + +<pre> +// File SingleSource.java + +RenderScript RS = RenderScript.create(context); +ScriptC_singlesource script = new ScriptC_singlesource(RS); +Allocation inputAllocation = Allocation.createFromBitmapResource( + RS, getResources(), R.drawable.image); +Allocation outputAllocation = Allocation.createTyped( + RS, inputAllocation.getType(), + Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_OUTPUT); +script.invoke_process(inputAllocation, outputAllocation); +</pre> + +<p>This example shows how an algorithm that involves two kernel launches can be implemented completely +in the RenderScript language itself. Without Single-Source +RenderScript, you would have to launch both kernels from the Java code, separating kernel launches +from kernel definitions and making it harder to understand the whole algorithm. Not only is the +Single-Source RenderScript code easier to read, it also eliminates the transitioning +between Java and the script across kernel launches. Some iterative algorithms may launch kernels +hundreds of times, making the overhead of such transitioning considerable.</p> + <h2 id="reduction-in-depth">Reduction Kernels in Depth</h2> <p><i>Reduction</i> is the process of combining a collection of data into a single diff --git a/drm/jni/Android.mk b/drm/jni/Android.mk index 08c7b953b813..d0797a9849cd 100644 --- a/drm/jni/Android.mk +++ b/drm/jni/Android.mk @@ -37,7 +37,6 @@ LOCAL_C_INCLUDES += \ $(TOP)/frameworks/av/drm/libdrmframework/include \ $(TOP)/frameworks/av/drm/libdrmframework/plugins/common/include \ $(TOP)/frameworks/av/include \ - $(TOP)/libcore/include LOCAL_MODULE_TAGS := optional diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h index 6eb2eef5a5f9..32ab501478f7 100644 --- a/libs/hwui/ClipArea.h +++ b/libs/hwui/ClipArea.h @@ -97,9 +97,9 @@ enum class ClipMode { }; struct ClipBase { - ClipBase(ClipMode mode) + explicit ClipBase(ClipMode mode) : mode(mode) {} - ClipBase(const Rect& rect) + explicit ClipBase(const Rect& rect) : mode(ClipMode::Rectangle) , rect(rect) {} const ClipMode mode; @@ -112,19 +112,19 @@ struct ClipBase { }; struct ClipRect : ClipBase { - ClipRect(const Rect& rect) + explicit ClipRect(const Rect& rect) : ClipBase(rect) {} }; struct ClipRectList : ClipBase { - ClipRectList(const RectangleList& rectList) + explicit ClipRectList(const RectangleList& rectList) : ClipBase(ClipMode::RectangleList) , rectList(rectList) {} RectangleList rectList; }; struct ClipRegion : ClipBase { - ClipRegion(const SkRegion& region) + explicit ClipRegion(const SkRegion& region) : ClipBase(ClipMode::Region) , region(region) {} ClipRegion() diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index 44a24c840892..40058223fb48 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -35,7 +35,7 @@ class DeferredLayerUpdater : public VirtualLightRefBase { public: // Note that DeferredLayerUpdater assumes it is taking ownership of the layer // and will not call incrementRef on it as a result. - ANDROID_API DeferredLayerUpdater(Layer* layer); + ANDROID_API explicit DeferredLayerUpdater(Layer* layer); ANDROID_API ~DeferredLayerUpdater(); ANDROID_API bool setSize(int width, int height) { diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index e10a81b8ccd8..d656864c5133 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -96,7 +96,7 @@ public: class FontRenderer { public: - FontRenderer(const uint8_t* gammaTable); + explicit FontRenderer(const uint8_t* gammaTable); ~FontRenderer(); void flushLargeCaches(std::vector<CacheTexture*>& cacheTextures); diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h index 851aeae8352c..bfb1bf1ba098 100644 --- a/libs/hwui/GpuMemoryTracker.h +++ b/libs/hwui/GpuMemoryTracker.h @@ -52,7 +52,7 @@ public: static void onFrameCompleted(); protected: - GpuMemoryTracker(GpuObjectType type) : mType(type) { + explicit GpuMemoryTracker(GpuObjectType type) : mType(type) { ASSERT_GPU_THREAD(); startTrackingObject(); } diff --git a/libs/hwui/PropertyValuesAnimatorSet.h b/libs/hwui/PropertyValuesAnimatorSet.h index c7ae7c0e8ce1..602fd91b0412 100644 --- a/libs/hwui/PropertyValuesAnimatorSet.h +++ b/libs/hwui/PropertyValuesAnimatorSet.h @@ -73,7 +73,7 @@ private: class PropertyAnimatorSetListener : public AnimationListener { public: - PropertyAnimatorSetListener(PropertyValuesAnimatorSet* set) : mSet(set) {} + explicit PropertyAnimatorSetListener(PropertyValuesAnimatorSet* set) : mSet(set) {} virtual void onAnimationFinished(BaseRenderNodeAnimator* animator) override; private: diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h index aee9d6370083..5497f863b357 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -509,7 +509,7 @@ struct LayerOp : RecordedOp { , mode(PaintUtils::getXfermodeDirect(paint)) , colorFilter(paint ? paint->getColorFilter() : nullptr) {} - LayerOp(RenderNode& node) + explicit LayerOp(RenderNode& node) : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), nullptr, nullptr) , layerHandle(node.getLayerHandle()) , alpha(node.properties().layerProperties().alpha() / 255.0f) diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index a5d1d4b86673..3586d8a1d967 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -90,7 +90,7 @@ class ANDROID_API Node { public: class Properties { public: - Properties(Node* node) : mNode(node) {} + explicit Properties(Node* node) : mNode(node) {} inline void onPropertyChanged() { mNode->onPropertyChanged(this); } @@ -132,7 +132,7 @@ public: class PathProperties : public Properties { public: - PathProperties(Node* node) : Properties(node) {} + explicit PathProperties(Node* node) : Properties(node) {} void syncProperties(const PathProperties& prop) { mData = prop.mData; onPropertyChanged(); @@ -218,7 +218,7 @@ public: float strokeMiterLimit = 4; int fillType = 0; /* non-zero or kWinding_FillType in Skia */ }; - FullPathProperties(Node* mNode) : Properties(mNode), mTrimDirty(false) {} + explicit FullPathProperties(Node* mNode) : Properties(mNode), mTrimDirty(false) {} ~FullPathProperties() { SkSafeUnref(fillGradient); SkSafeUnref(strokeGradient); @@ -409,7 +409,7 @@ class ANDROID_API Group: public Node { public: class GroupProperties : public Properties { public: - GroupProperties(Node* mNode) : Properties(mNode) {} + explicit GroupProperties(Node* mNode) : Properties(mNode) {} struct PrimitiveFields { float rotate = 0; float pivotX = 0; @@ -539,7 +539,7 @@ private: class ANDROID_API Tree : public VirtualLightRefBase { public: - Tree(Group* rootNode) : mRootNode(rootNode) { + explicit Tree(Group* rootNode) : mRootNode(rootNode) { mRootNode->setPropertyChangedListener(&mPropertyChangedListener); } @@ -576,7 +576,7 @@ public: class TreeProperties { public: - TreeProperties(Tree* tree) : mTree(tree) {} + explicit TreeProperties(Tree* tree) : mTree(tree) {} // Properties that can only be modified by UI thread, therefore sync should // only go from UI to RT struct NonAnimatableProperties { diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index 9599c30c639b..f72a6321643a 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -30,7 +30,7 @@ class ANDROID_API Paint : public SkPaint { public: Paint(); Paint(const Paint& paint); - Paint(const SkPaint& paint); + Paint(const SkPaint& paint); // NOLINT(implicit) ~Paint(); Paint& operator=(const Paint& other); diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h index 73a3392a7811..26d4e3654a48 100644 --- a/libs/hwui/renderstate/OffscreenBufferPool.h +++ b/libs/hwui/renderstate/OffscreenBufferPool.h @@ -126,7 +126,7 @@ private: : width(OffscreenBuffer::computeIdealDimension(layerWidth)) , height(OffscreenBuffer::computeIdealDimension(layerHeight)) {} - Entry(OffscreenBuffer* layer) + explicit Entry(OffscreenBuffer* layer) : layer(layer) , width(layer->texture.width()) , height(layer->texture.height()) { diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h index 706f2ff75222..060984ea9f32 100644 --- a/libs/hwui/tests/common/TestScene.h +++ b/libs/hwui/tests/common/TestScene.h @@ -55,7 +55,7 @@ public: class Registrar { public: - Registrar(const TestScene::Info& info) { + explicit Registrar(const TestScene::Info& info) { TestScene::registerScene(info); } private: diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index a48469c68b3d..5a4ab99012b9 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -102,7 +102,7 @@ public: public: SignalingDtor() : mSignal(nullptr) {} - SignalingDtor(int* signal) + explicit SignalingDtor(int* signal) : mSignal(signal) {} void setSignal(int* signal) { mSignal = signal; @@ -202,7 +202,7 @@ public: class TestTask : public renderthread::RenderTask { public: - TestTask(RtCallback rtCallback) + explicit TestTask(RtCallback rtCallback) : rtCallback(rtCallback) {} virtual ~TestTask() {} virtual void run() override; diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h index 93d37c28f8a4..df8cb076e763 100644 --- a/libs/hwui/utils/FatVector.h +++ b/libs/hwui/utils/FatVector.h @@ -53,7 +53,7 @@ public: typedef T value_type; // needed to implement std::allocator typedef T* pointer; // needed to implement std::allocator - InlineStdAllocator(Allocation& allocation) + explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {} InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {} @@ -93,7 +93,7 @@ public: this->reserve(SIZE); } - FatVector(size_t capacity) : FatVector() { + explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); } diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h index 34c8c6beea81..f95a6fe5060e 100644 --- a/libs/hwui/utils/LinearAllocator.h +++ b/libs/hwui/utils/LinearAllocator.h @@ -157,7 +157,7 @@ public: typedef T value_type; // needed to implement std::allocator typedef T* pointer; // needed to implement std::allocator - LinearStdAllocator(LinearAllocator& allocator) + explicit LinearStdAllocator(LinearAllocator& allocator) : linearAllocator(allocator) {} LinearStdAllocator(const LinearStdAllocator& other) : linearAllocator(other.linearAllocator) {} @@ -170,7 +170,7 @@ public: }; // enable allocators to be constructed from other templated types template <class U> - LinearStdAllocator(const LinearStdAllocator<U>& other) + LinearStdAllocator(const LinearStdAllocator<U>& other) // NOLINT(implicit) : linearAllocator(other.linearAllocator) {} T* allocate(size_t num, const void* = 0) { @@ -195,7 +195,7 @@ bool operator!= (const LinearStdAllocator<T1>&, const LinearStdAllocator<T2>&) { template <class T> class LsaVector : public std::vector<T, LinearStdAllocator<T>> { public: - LsaVector(const LinearStdAllocator<T>& allocator) + explicit LsaVector(const LinearStdAllocator<T>& allocator) : std::vector<T, LinearStdAllocator<T>>(allocator) {} }; diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h index 905833c7877d..7fc8d6fd5197 100644 --- a/libs/input/SpriteController.h +++ b/libs/input/SpriteController.h @@ -214,7 +214,7 @@ private: virtual ~SpriteImpl(); public: - SpriteImpl(const sp<SpriteController> controller); + explicit SpriteImpl(const sp<SpriteController> controller); virtual void setIcon(const SpriteIcon& icon); virtual void setVisible(bool visible); diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h index 8184f943ce23..39c155439767 100644 --- a/media/jni/android_media_Utils.h +++ b/media/jni/android_media_Utils.h @@ -37,7 +37,7 @@ private: size_t mPosition; public: - AssetStream(SkStream* stream); + explicit AssetStream(SkStream* stream); ~AssetStream(); // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer @@ -60,7 +60,7 @@ private: const size_t kMinSizeToRead = 8192; public: - BufferedStream(SkStream* stream); + explicit BufferedStream(SkStream* stream); ~BufferedStream(); // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer @@ -79,8 +79,8 @@ private: size_t mPosition; public: - FileStream(const int fd); - FileStream(const String8 filename); + explicit FileStream(const int fd); + explicit FileStream(const String8 filename); ~FileStream(); // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer diff --git a/media/mca/filterfw/native/core/gl_frame.h b/media/mca/filterfw/native/core/gl_frame.h index f2a1ad5ed81b..fdbb1f57009a 100644 --- a/media/mca/filterfw/native/core/gl_frame.h +++ b/media/mca/filterfw/native/core/gl_frame.h @@ -38,7 +38,7 @@ class GLFrame : public GLBufferHandle { // Create an empty GL frame in the specified GL environment. Note, that the GLFrame does NOT // take ownership. The caller must make sure the GLEnv stays valid as long as the GLFrame is // alive. - GLFrame(GLEnv* gl_env); + explicit GLFrame(GLEnv* gl_env); // Deallocate a GL frame. ~GLFrame(); diff --git a/media/mca/filterfw/native/core/native_frame.h b/media/mca/filterfw/native/core/native_frame.h index 0d335b36a26b..2da557dec000 100644 --- a/media/mca/filterfw/native/core/native_frame.h +++ b/media/mca/filterfw/native/core/native_frame.h @@ -27,7 +27,7 @@ namespace filterfw { class NativeFrame { public: // Create an empty native frame. - NativeFrame(int size); + explicit NativeFrame(int size); ~NativeFrame(); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 39a341216f2c..9df5690d9534 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -16,11 +16,13 @@ package com.android.systemui; +import android.app.ActivityThread; import android.app.Application; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.os.Process; import android.os.SystemProperties; @@ -105,6 +107,13 @@ public class SystemUIApplication extends Application { } }, filter); } else { + // We don't need to startServices for sub-process that is doing some tasks. + // (screenshots, sweetsweetdesserts or tuner ..) + String processName = ActivityThread.currentProcessName(); + ApplicationInfo info = getApplicationInfo(); + if (processName != null && processName.startsWith(info.processName + ":")) { + return; + } // For a secondary user, boot-completed will never be called because it has already // been broadcasted on startup for the primary SystemUI process. Instead, for // components which require the SystemUI component to be initialized per-user, we diff --git a/services/core/Android.mk b/services/core/Android.mk index b965ce39e7d5..a5b1069974f2 100644 --- a/services/core/Android.mk +++ b/services/core/Android.mk @@ -11,7 +11,7 @@ LOCAL_SRC_FILES += \ java/com/android/server/EventLogTags.logtags \ java/com/android/server/am/EventLogTags.logtags \ ../../../../system/netd/server/binder/android/net/INetd.aidl \ - ../../../../system/netd/server/binder/android/net/metrics/IDnsEventListener.aidl \ + ../../../../system/netd/server/binder/android/net/metrics/INetdEventListener.aidl \ LOCAL_AIDL_INCLUDES += \ system/netd/server/binder diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index cdd977be29ed..2cf93ab561c5 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -922,7 +922,7 @@ class MountService extends IMountService.Stub // Record user as started so newly mounted volumes kick off events // correctly, then synthesize events for any already-mounted volumes. - synchronized (mVolumes) { + synchronized (mLock) { for (int i = 0; i < mVolumes.size(); i++) { final VolumeInfo vol = mVolumes.valueAt(i); if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) { @@ -945,7 +945,7 @@ class MountService extends IMountService.Stub } catch (NativeDaemonConnectorException ignored) { } - synchronized (mVolumes) { + synchronized (mLock) { mSystemUnlockedUsers = ArrayUtils.removeInt(mSystemUnlockedUsers, userId); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 08b204af7fe7..867168a07a03 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6703,7 +6703,7 @@ public final class ActivityManagerService extends ActivityManagerNative ArraySet<String> completedIsas = new ArraySet<String>(); for (String abi : Build.SUPPORTED_ABIS) { - Process.establishZygoteConnectionForAbi(abi); + Process.zygoteProcess.establishZygoteConnectionForAbi(abi); final String instructionSet = VMRuntime.getInstructionSet(abi); if (!completedIsas.contains(instructionSet)) { try { diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 49106f42044e..ba799f2ca3d1 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -753,6 +753,12 @@ class AppErrors { } else if (app.crashing) { Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation); return; + } else if (app.killedByAm) { + Slog.i(TAG, "App already killed by AM skipping ANR: " + app + " " + annotation); + return; + } else if (app.killed) { + Slog.i(TAG, "Skipping died app ANR: " + app + " " + annotation); + return; } // In case we come through here for the same app before completing diff --git a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java index 69ef30fbe66a..3675b3e217d2 100644 --- a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java +++ b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java @@ -55,8 +55,8 @@ public class MetricsLoggerService extends SystemService { if (DBG) Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY"); publishBinderService(ConnectivityMetricsLogger.CONNECTIVITY_METRICS_LOGGER_SERVICE, mBinder); - mDnsListener = new DnsEventListenerService(getContext()); - publishBinderService(mDnsListener.SERVICE_NAME, mDnsListener); + mNetdListener = new NetdEventListenerService(getContext()); + publishBinderService(mNetdListener.SERVICE_NAME, mNetdListener); } } @@ -91,7 +91,7 @@ public class MetricsLoggerService extends SystemService { private final ArrayDeque<ConnectivityMetricsEvent> mEvents = new ArrayDeque<>(); - private DnsEventListenerService mDnsListener; + private NetdEventListenerService mNetdListener; private void enforceConnectivityInternalPermission() { getContext().enforceCallingOrSelfPermission( @@ -223,7 +223,7 @@ public class MetricsLoggerService extends SystemService { } pw.println(); - mDnsListener.dump(pw); + mNetdListener.dump(pw); } public long logEvent(ConnectivityMetricsEvent event) { diff --git a/services/core/java/com/android/server/connectivity/DnsEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java index 18ab73100b81..98f34ca11f71 100644 --- a/services/core/java/com/android/server/connectivity/DnsEventListenerService.java +++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java @@ -22,7 +22,7 @@ import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.NetworkRequest; -import android.net.metrics.IDnsEventListener; +import android.net.metrics.INetdEventListener; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -35,13 +35,13 @@ import java.util.TreeMap; /** - * Implementation of the IDnsEventListener interface. + * Implementation of the INetdEventListener interface. */ -public class DnsEventListenerService extends IDnsEventListener.Stub { +public class NetdEventListenerService extends INetdEventListener.Stub { - public static final String SERVICE_NAME = "dns_listener"; + public static final String SERVICE_NAME = "netd_listener"; - private static final String TAG = DnsEventListenerService.class.getSimpleName(); + private static final String TAG = NetdEventListenerService.class.getSimpleName(); private static final boolean DBG = true; private static final boolean VDBG = false; @@ -106,7 +106,7 @@ public class DnsEventListenerService extends IDnsEventListener.Stub { private final NetworkCallback mNetworkCallback = new NetworkCallback() { @Override public void onLost(Network network) { - synchronized (DnsEventListenerService.this) { + synchronized (NetdEventListenerService.this) { DnsEventBatch batch = mEventBatches.remove(network.netId); if (batch != null) { batch.logAndClear(); @@ -115,7 +115,7 @@ public class DnsEventListenerService extends IDnsEventListener.Stub { } }; - public DnsEventListenerService(Context context) { + public NetdEventListenerService(Context context) { // We are started when boot is complete, so ConnectivityService should already be running. final NetworkRequest request = new NetworkRequest.Builder() .clearCapabilities() diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java index 50bb022ef3f0..76ad9d75bb2d 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java @@ -136,10 +136,17 @@ public class TetherInterfaceStateMachine extends StateMachine { if (ifcg != null) { InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString); ifcg.setLinkAddress(new LinkAddress(addr, prefixLen)); - if (enabled) { - ifcg.setInterfaceUp(); + if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) { + // The WiFi stack has ownership of the interface up/down state. + // It is unclear whether the bluetooth or USB stacks will manage their own + // state. + ifcg.ignoreInterfaceUpDownStatus(); } else { - ifcg.setInterfaceDown(); + if (enabled) { + ifcg.setInterfaceUp(); + } else { + ifcg.setInterfaceDown(); + } } ifcg.clearFlag("running"); mNMService.setInterfaceConfig(mIfaceName, ifcg); diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 330f46a3c95a..5ae408bd01eb 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -163,7 +163,6 @@ import android.util.Xml; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.content.PackageMonitor; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; @@ -366,7 +365,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final AppOpsManager mAppOps; - private final MyPackageMonitor mPackageMonitor; private final IPackageManager mIPm; @@ -411,8 +409,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mAppOps = context.getSystemService(AppOpsManager.class); - mPackageMonitor = new MyPackageMonitor(); - // Expose private service for system components to use. LocalServices.addService(NetworkPolicyManagerInternal.class, new NetworkPolicyManagerInternalImpl()); @@ -540,8 +536,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); - mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true); - synchronized (mRulesLock) { updatePowerSaveWhitelistLocked(); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); @@ -731,6 +725,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (LOGV) Slog.v(TAG, "ACTION_UID_REMOVED for uid=" + uid); synchronized (mRulesLock) { mUidPolicy.delete(uid); + removeRestrictBackgroundWhitelistedUidLocked(uid, true, true); updateRestrictionRulesForUidLocked(uid); writePolicyLocked(); } @@ -3479,18 +3474,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private class MyPackageMonitor extends PackageMonitor { - - @Override - public void onPackageRemoved(String packageName, int uid) { - if (LOGV) Slog.v(TAG, "onPackageRemoved: " + packageName + " ->" + uid); - synchronized (mRulesLock) { - removeRestrictBackgroundWhitelistedUidLocked(uid, true, true); - updateRestrictionRulesForUidLocked(uid); - } - } - } - private class NetworkPolicyManagerInternalImpl extends NetworkPolicyManagerInternal { @Override diff --git a/services/core/java/com/android/server/pm/AbstractStatsBase.java b/services/core/java/com/android/server/pm/AbstractStatsBase.java new file mode 100644 index 000000000000..612c4767ccaa --- /dev/null +++ b/services/core/java/com/android/server/pm/AbstractStatsBase.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.os.Environment; +import android.os.SystemClock; +import android.util.AtomicFile; + +import java.io.File; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +/** + * A simple base class for statistics that need to be saved/restored from a dedicated file. This + * class provides a base implementation that: + * <ul> + * <li>Provide an AtomicFile to the actual read/write code + * <li>A background-thread write and a synchronous write + * <li>Write-limiting for the background-thread (by default writes are at least 30 minutes apart) + * <li>Can lock on the provided data object before writing + * </ul> + * For completion, a subclass needs to implement actual {@link #writeInternal(Object) writing} and + * {@link #readInternal(Object) reading}. + */ +public abstract class AbstractStatsBase<T> { + + private static final int WRITE_INTERVAL_MS = + (PackageManagerService.DEBUG_DEXOPT) ? 0 : 30*60*1000; + private final Object mFileLock = new Object(); + private final AtomicLong mLastTimeWritten = new AtomicLong(0); + private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false); + private final String mFileName; + private final String mBackgroundThreadName; + private final boolean mLock; + + protected AbstractStatsBase(String fileName, String threadName, boolean lock) { + mFileName = fileName; + mBackgroundThreadName = threadName; + mLock = lock; + } + + protected AtomicFile getFile() { + File dataDir = Environment.getDataDirectory(); + File systemDir = new File(dataDir, "system"); + File fname = new File(systemDir, mFileName); + return new AtomicFile(fname); + } + + void writeNow(final T data) { + writeImpl(data); + mLastTimeWritten.set(SystemClock.elapsedRealtime()); + } + + boolean maybeWriteAsync(final T data) { + if (SystemClock.elapsedRealtime() - mLastTimeWritten.get() < WRITE_INTERVAL_MS + && !PackageManagerService.DEBUG_DEXOPT) { + return false; + } + + if (mBackgroundWriteRunning.compareAndSet(false, true)) { + new Thread(mBackgroundThreadName) { + @Override + public void run() { + try { + writeImpl(data); + mLastTimeWritten.set(SystemClock.elapsedRealtime()); + } finally { + mBackgroundWriteRunning.set(false); + } + } + }.start(); + return true; + } + + return false; + } + + private void writeImpl(T data) { + if (mLock) { + synchronized (data) { + synchronized (mFileLock) { + writeInternal(data); + } + } + } else { + synchronized (mFileLock) { + writeInternal(data); + } + } + } + + protected abstract void writeInternal(T data); + + void read(T data) { + if (mLock) { + synchronized (data) { + synchronized (mFileLock) { + readInternal(data); + } + } + } else { + synchronized (mFileLock) { + readInternal(data); + } + } + // We use the current time as last-written. read() is called on system server startup + // (current situation), and we want to postpone I/O at boot. + mLastTimeWritten.set(SystemClock.elapsedRealtime()); + } + + protected abstract void readInternal(T data); +} diff --git a/services/core/java/com/android/server/pm/CompilerStats.java b/services/core/java/com/android/server/pm/CompilerStats.java new file mode 100644 index 000000000000..46efd98fdc6b --- /dev/null +++ b/services/core/java/com/android/server/pm/CompilerStats.java @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.util.ArrayMap; +import android.util.AtomicFile; +import android.util.Log; + +import com.android.internal.util.FastPrintWriter; +import com.android.internal.util.IndentingPrintWriter; + +import libcore.io.IoUtils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.HashMap; +import java.util.Map; + +/** + * A class that collects, serializes and deserializes compiler-related statistics on a + * per-package per-code-path basis. + * + * Currently used to track compile times. + */ +class CompilerStats extends AbstractStatsBase<Void> { + + private final static String COMPILER_STATS_VERSION_HEADER = "PACKAGE_MANAGER__COMPILER_STATS__"; + private final static int COMPILER_STATS_VERSION = 1; + + /** + * Class to collect all stats pertaining to one package. + */ + static class PackageStats { + + private final String packageName; + + /** + * This map stores compile-times for all code paths in the package. The value + * is in milliseconds. + */ + private final Map<String, Long> compileTimePerCodePath; + + /** + * @param packageName + */ + public PackageStats(String packageName) { + this.packageName = packageName; + // We expect at least one element in here, but let's make it minimal. + compileTimePerCodePath = new ArrayMap<>(2); + } + + public String getPackageName() { + return packageName; + } + + /** + * Return the recorded compile time for a given code path. Returns + * 0 if there is no recorded time. + */ + public long getCompileTime(String codePath) { + String storagePath = getStoredPathFromCodePath(codePath); + synchronized (compileTimePerCodePath) { + Long l = compileTimePerCodePath.get(storagePath); + if (l == null) { + return 0; + } + return l; + } + } + + public void setCompileTime(String codePath, long compileTimeInMs) { + String storagePath = getStoredPathFromCodePath(codePath); + synchronized (compileTimePerCodePath) { + if (compileTimeInMs <= 0) { + compileTimePerCodePath.remove(storagePath); + } else { + compileTimePerCodePath.put(storagePath, compileTimeInMs); + } + } + } + + private static String getStoredPathFromCodePath(String codePath) { + int lastSlash = codePath.lastIndexOf(File.separatorChar); + return codePath.substring(lastSlash + 1); + } + + public void dump(IndentingPrintWriter ipw) { + synchronized (compileTimePerCodePath) { + if (compileTimePerCodePath.size() == 0) { + ipw.println("(No recorded stats)"); + } else { + for (Map.Entry<String, Long> e : compileTimePerCodePath.entrySet()) { + ipw.println(" " + e.getKey() + " - " + e.getValue()); + } + } + } + } + } + + private final Map<String, PackageStats> packageStats; + + public CompilerStats() { + super("package-cstats.list", "CompilerStats_DiskWriter", /* lock */ false); + packageStats = new HashMap<>(); + } + + public PackageStats getPackageStats(String packageName) { + synchronized (packageStats) { + return packageStats.get(packageName); + } + } + + public void setPackageStats(String packageName, PackageStats stats) { + synchronized (packageStats) { + packageStats.put(packageName, stats); + } + } + + public PackageStats createPackageStats(String packageName) { + synchronized (packageStats) { + PackageStats newStats = new PackageStats(packageName); + packageStats.put(packageName, newStats); + return newStats; + } + } + + public PackageStats getOrCreatePackageStats(String packageName) { + synchronized (packageStats) { + PackageStats existingStats = packageStats.get(packageName); + if (existingStats != null) { + return existingStats; + } + + return createPackageStats(packageName); + } + } + + public void deletePackageStats(String packageName) { + synchronized (packageStats) { + packageStats.remove(packageName); + } + } + + // I/O + + // The encoding is simple: + // + // 1) The first line is a line consisting of the version header and the version number. + // + // 2) The rest of the file is package data. + // 2.1) A package is started by any line not starting with "-"; + // 2.2) Any line starting with "-" is code path data. The format is: + // '-'{code-path}':'{compile-time} + + public void write(Writer out) { + @SuppressWarnings("resource") + FastPrintWriter fpw = new FastPrintWriter(out); + + fpw.print(COMPILER_STATS_VERSION_HEADER); + fpw.println(COMPILER_STATS_VERSION); + + synchronized (packageStats) { + for (PackageStats pkg : packageStats.values()) { + synchronized (pkg.compileTimePerCodePath) { + if (!pkg.compileTimePerCodePath.isEmpty()) { + fpw.println(pkg.getPackageName()); + + for (Map.Entry<String, Long> e : pkg.compileTimePerCodePath.entrySet()) { + fpw.println("-" + e.getKey() + ":" + e.getValue()); + } + } + } + } + } + + fpw.flush(); + } + + public boolean read(Reader r) { + synchronized (packageStats) { + // TODO: Could make this a final switch, then we wouldn't have to synchronize over + // the whole reading. + packageStats.clear(); + + try { + BufferedReader in = new BufferedReader(r); + + // Read header, do version check. + String versionLine = in.readLine(); + if (versionLine == null) { + throw new IllegalArgumentException("No version line found."); + } else { + if (!versionLine.startsWith(COMPILER_STATS_VERSION_HEADER)) { + throw new IllegalArgumentException("Invalid version line: " + versionLine); + } + int version = Integer.parseInt( + versionLine.substring(COMPILER_STATS_VERSION_HEADER.length())); + if (version != COMPILER_STATS_VERSION) { + // TODO: Upgrade older formats? For now, just reject and regenerate. + throw new IllegalArgumentException("Unexpected version: " + version); + } + } + + // For simpler code, we ignore any data lines before the first package. We + // collect it in a fake package. + PackageStats currentPackage = new PackageStats("fake package"); + + String s = null; + while ((s = in.readLine()) != null) { + if (s.startsWith("-")) { + int colonIndex = s.indexOf(':'); + if (colonIndex == -1 || colonIndex == 1) { + throw new IllegalArgumentException("Could not parse data " + s); + } + String codePath = s.substring(1, colonIndex); + long time = Long.parseLong(s.substring(colonIndex + 1)); + currentPackage.setCompileTime(codePath, time); + } else { + currentPackage = getOrCreatePackageStats(s); + } + } + } catch (Exception e) { + Log.e(PackageManagerService.TAG, "Error parsing compiler stats", e); + return false; + } + + return true; + } + } + + void writeNow() { + writeNow(null); + } + + boolean maybeWriteAsync() { + return maybeWriteAsync(null); + } + + @Override + protected void writeInternal(Void data) { + AtomicFile file = getFile(); + FileOutputStream f = null; + + try { + f = file.startWrite(); + OutputStreamWriter osw = new OutputStreamWriter(f); + osw.flush(); + file.finishWrite(f); + } catch (IOException e) { + if (f != null) { + file.failWrite(f); + } + Log.e(PackageManagerService.TAG, "Failed to write compiler stats", e); + } + } + + void read() { + read((Void)null); + } + + @Override + protected void readInternal(Void data) { + AtomicFile file = getFile(); + BufferedReader in = null; + try { + in = new BufferedReader(new InputStreamReader(file.openRead())); + read(in); + } catch (FileNotFoundException expected) { + } finally { + IoUtils.closeQuietly(in); + } + } +} diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 7b85a4f25cee..72c549f7bec6 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -61,6 +61,13 @@ public final class Installer extends SystemService { mInstaller = new InstallerConnection(); } + // Package-private installer that accepts a custom InstallerConnection. Used for + // OtaDexoptService. + Installer(Context context, InstallerConnection connection) { + super(context); + mInstaller = connection; + } + /** * Yell loudly if someone tries making future calls while holding a lock on * the given object. diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 649a27cb7cd6..bff6d2d4786e 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -32,10 +32,12 @@ import android.os.storage.StorageManager; import android.util.Log; import android.util.Slog; +import com.android.internal.os.InstallerConnection; import com.android.internal.os.InstallerConnection.InstallerException; import java.io.File; import java.io.FileDescriptor; +import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -48,21 +50,25 @@ public class OtaDexoptService extends IOtaDexopt.Stub { private final static String TAG = "OTADexopt"; private final static boolean DEBUG_DEXOPT = true; + // The synthetic library dependencies denoting "no checks." + private final static String[] NO_LIBRARIES = new String[] { "&" }; + private final Context mContext; - private final PackageDexOptimizer mPackageDexOptimizer; private final PackageManagerService mPackageManagerService; // TODO: Evaluate the need for WeakReferences here. - private List<PackageParser.Package> mDexoptPackages; + + /** + * The list of dexopt invocations for all work. + */ + private List<String> mDexoptCommands; + + private int completeSize; public OtaDexoptService(Context context, PackageManagerService packageManagerService) { this.mContext = context; this.mPackageManagerService = packageManagerService; - // Use the package manager install and install lock here for the OTA dex optimizer. - mPackageDexOptimizer = new OTADexoptPackageDexOptimizer(packageManagerService.mInstaller, - packageManagerService.mInstallLock, context); - // Now it's time to check whether we need to move any A/B artifacts. moveAbArtifacts(packageManagerService.mInstaller); } @@ -84,13 +90,44 @@ public class OtaDexoptService extends IOtaDexopt.Stub { @Override public synchronized void prepare() throws RemoteException { - if (mDexoptPackages != null) { + if (mDexoptCommands != null) { throw new IllegalStateException("already called prepare()"); } + final List<PackageParser.Package> important; + final List<PackageParser.Package> others; synchronized (mPackageManagerService.mPackages) { - mDexoptPackages = PackageManagerServiceUtils.getPackagesForDexopt( + // Important: the packages we need to run with ab-ota compiler-reason. + important = PackageManagerServiceUtils.getPackagesForDexopt( mPackageManagerService.mPackages.values(), mPackageManagerService); + // Others: we should optimize this with the (first-)boot compiler-reason. + others = new ArrayList<>(mPackageManagerService.mPackages.values()); + others.removeAll(important); + + // Pre-size the array list by over-allocating by a factor of 1.5. + mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2); } + + for (PackageParser.Package p : important) { + // Make sure that core apps are optimized according to their own "reason". + // If the core apps are not preopted in the B OTA, and REASON_AB_OTA is not speed + // (by default is speed-profile) they will be interepreted/JITed. This in itself is + // not a problem as we will end up doing profile guided compilation. However, some + // core apps may be loaded by system server which doesn't JIT and we need to make + // sure we don't interpret-only + int compilationReason = p.coreApp + ? PackageManagerService.REASON_CORE_APP + : PackageManagerService.REASON_AB_OTA; + mDexoptCommands.addAll(generatePackageDexopts(p, compilationReason)); + } + for (PackageParser.Package p : others) { + // We assume here that there are no core apps left. + if (p.coreApp) { + throw new IllegalStateException("Found a core app that's not important"); + } + mDexoptCommands.addAll( + generatePackageDexopts(p, PackageManagerService.REASON_FIRST_BOOT)); + } + completeSize = mDexoptCommands.size(); } @Override @@ -98,35 +135,52 @@ public class OtaDexoptService extends IOtaDexopt.Stub { if (DEBUG_DEXOPT) { Log.i(TAG, "Cleaning up OTA Dexopt state."); } - mDexoptPackages = null; + mDexoptCommands = null; } @Override public synchronized boolean isDone() throws RemoteException { - if (mDexoptPackages == null) { + if (mDexoptCommands == null) { throw new IllegalStateException("done() called before prepare()"); } - return mDexoptPackages.isEmpty(); + return mDexoptCommands.isEmpty(); } @Override - public synchronized void dexoptNextPackage() throws RemoteException { - if (mDexoptPackages == null) { + public synchronized float getProgress() throws RemoteException { + // Approximate the progress by the amount of already completed commands. + if (completeSize == 0) { + return 1f; + } + int commandsLeft = mDexoptCommands.size(); + return (completeSize - commandsLeft) / ((float)completeSize); + } + + @Override + public synchronized String nextDexoptCommand() throws RemoteException { + if (mDexoptCommands == null) { throw new IllegalStateException("dexoptNextPackage() called before prepare()"); } - if (mDexoptPackages.isEmpty()) { - // Tolerate repeated calls. - return; + + if (mDexoptCommands.isEmpty()) { + return "(all done)"; } - PackageParser.Package nextPackage = mDexoptPackages.remove(0); + String next = mDexoptCommands.remove(0); - if (DEBUG_DEXOPT) { - Log.i(TAG, "Processing " + nextPackage.packageName + " for OTA dexopt."); + if (IsFreeSpaceAvailable()) { + return next; + } else { + mDexoptCommands.clear(); + return "(no free space)"; } + } - // Check for low space. + /** + * Check for low space. Returns true if there's space left. + */ + private boolean IsFreeSpaceAvailable() { // TODO: If apps are not installed in the internal /data partition, we should compare // against that storage's free capacity. File dataDir = Environment.getDataDirectory(); @@ -136,19 +190,43 @@ public class OtaDexoptService extends IOtaDexopt.Stub { throw new IllegalStateException("Invalid low memory threshold"); } long usableSpace = dataDir.getUsableSpace(); - if (usableSpace < lowThreshold) { - Log.w(TAG, "Not running dexopt on " + nextPackage.packageName + " due to low memory: " + - usableSpace); - return; + return (usableSpace >= lowThreshold); + } + + /** + * Generate all dexopt commands for the given package. + */ + private synchronized List<String> generatePackageDexopts(PackageParser.Package pkg, + int compilationReason) { + // Use our custom connection that just collects the commands. + RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection(); + Installer collectingInstaller = new Installer(mContext, collectingConnection); + + // Use the package manager install and install lock here for the OTA dex optimizer. + PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer( + collectingInstaller, mPackageManagerService.mInstallLock, mContext); + + String[] libraryDependencies = pkg.usesLibraryFiles; + if (pkg.isSystemApp()) { + // For system apps, we want to avoid classpaths checks. + libraryDependencies = NO_LIBRARIES; } - mPackageDexOptimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles, + optimizer.performDexOpt(pkg, libraryDependencies, null /* ISAs */, false /* checkProfiles */, - getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA)); + getCompilerFilterForReason(compilationReason), + null /* CompilerStats.PackageStats */); + + return collectingConnection.commands; + } + + @Override + public synchronized void dexoptNextPackage() throws RemoteException { + throw new UnsupportedOperationException(); } private void moveAbArtifacts(Installer installer) { - if (mDexoptPackages != null) { + if (mDexoptCommands != null) { throw new IllegalStateException("Should not be ota-dexopting when trying to move."); } @@ -208,4 +286,40 @@ public class OtaDexoptService extends IOtaDexopt.Stub { } } + + private static class RecordingInstallerConnection extends InstallerConnection { + public List<String> commands = new ArrayList<String>(1); + + @Override + public void setWarnIfHeld(Object warnIfHeld) { + throw new IllegalStateException("Should not reach here"); + } + + @Override + public synchronized String transact(String cmd) { + commands.add(cmd); + return "0"; + } + + @Override + public boolean mergeProfiles(int uid, String pkgName) throws InstallerException { + throw new IllegalStateException("Should not reach here"); + } + + @Override + public boolean dumpProfiles(String gid, String packageName, String codePaths) + throws InstallerException { + throw new IllegalStateException("Should not reach here"); + } + + @Override + public void disconnect() { + throw new IllegalStateException("Should not reach here"); + } + + @Override + public void waitForConnection() { + throw new IllegalStateException("Should not reach here"); + } + } } diff --git a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java index ea9cf1766232..bbd404879a21 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java +++ b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java @@ -46,6 +46,10 @@ class OtaDexoptShellCommand extends ShellCommand { return runOtaDone(); case "step": return runOtaStep(); + case "next": + return runOtaNext(); + case "progress": + return runOtaProgress(); default: return handleDefaultCommands(cmd); } @@ -81,6 +85,18 @@ class OtaDexoptShellCommand extends ShellCommand { return 0; } + private int runOtaNext() throws RemoteException { + getOutPrintWriter().println(mInterface.nextDexoptCommand()); + return 0; + } + + private int runOtaProgress() throws RemoteException { + final float progress = mInterface.getProgress(); + final PrintWriter pw = getOutPrintWriter(); + pw.format("%.2f", progress); + return 0; + } + @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); @@ -94,6 +110,8 @@ class OtaDexoptShellCommand extends ShellCommand { pw.println(" Replies whether the OTA is complete or not."); pw.println(" step"); pw.println(" OTA dexopt the next package."); + pw.println(" next"); + pw.println(" Get parameters for OTA dexopt of the next package."); pw.println(" cleanup"); pw.println(" Clean up internal states. Ends an OTA session."); } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 26a840da094b..1ef4a9fa5849 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -90,7 +90,8 @@ class PackageDexOptimizer { * synchronized on {@link #mInstallLock}. */ int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries, - String[] instructionSets, boolean checkProfiles, String targetCompilationFilter) { + String[] instructionSets, boolean checkProfiles, String targetCompilationFilter, + CompilerStats.PackageStats packageStats) { synchronized (mInstallLock) { final boolean useLock = mSystemReady; if (useLock) { @@ -99,7 +100,7 @@ class PackageDexOptimizer { } try { return performDexOptLI(pkg, sharedLibraries, instructionSets, checkProfiles, - targetCompilationFilter); + targetCompilationFilter, packageStats); } finally { if (useLock) { mDexoptWakeLock.release(); @@ -150,7 +151,8 @@ class PackageDexOptimizer { } private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries, - String[] targetInstructionSets, boolean checkProfiles, String targetCompilerFilter) { + String[] targetInstructionSets, boolean checkProfiles, String targetCompilerFilter, + CompilerStats.PackageStats packageStats) { final String[] instructionSets = targetInstructionSets != null ? targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo); @@ -174,6 +176,16 @@ class PackageDexOptimizer { isProfileGuidedFilter = false; } + // Disable profile guided compilation for vmSafeMode. + final boolean vmSafeMode = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) + != 0; + final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) + != 0; + if (vmSafeMode) { + targetCompilerFilter = getNonProfileGuidedCompilerFilter(targetCompilerFilter); + isProfileGuidedFilter = false; + } + // If we're asked to take profile updates into account, check now. boolean newProfile = false; if (checkProfiles && isProfileGuidedFilter) { @@ -185,9 +197,6 @@ class PackageDexOptimizer { } } - final boolean vmSafeMode = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0; - final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; - boolean performedDexOpt = false; boolean successfulDexOpt = true; @@ -254,10 +263,17 @@ class PackageDexOptimizer { | DEXOPT_BOOTCOMPLETE); try { + long startTime = System.currentTimeMillis(); + mInstaller.dexopt(path, sharedGid, pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, dexFlags, targetCompilerFilter, pkg.volumeUuid, sharedLibrariesPath); performedDexOpt = true; + + if (packageStats != null) { + long endTime = System.currentTimeMillis(); + packageStats.setCompileTime(path, (int)(endTime - startTime)); + } } catch (InstallerException e) { Slog.w(TAG, "Failed to dexopt", e); successfulDexOpt = false; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 00199730b0b7..76d987b54e44 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -76,8 +76,6 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageParser.PARSE_IS_PRIVILEGED; import static android.content.pm.PackageParser.isApkFile; -import static android.os.Process.PACKAGE_INFO_GID; -import static android.os.Process.SYSTEM_UID; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDWR; @@ -95,6 +93,7 @@ import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet; import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason; import static com.android.server.pm.PackageManagerServiceCompilerMapping.getFullCompilerFilter; +import static com.android.server.pm.PackageManagerServiceCompilerMapping.getNonProfileGuidedCompilerFilter; import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE; import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS; import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; @@ -205,7 +204,6 @@ import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.AtomicFile; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.ExceptionUtils; @@ -264,7 +262,6 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; @@ -277,7 +274,6 @@ import java.io.FileOutputStream; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; -import java.io.InputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.security.DigestInputStream; @@ -304,7 +300,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; /** * Keep track of all those APKs everywhere. @@ -624,7 +619,7 @@ public class PackageManagerService extends IPackageManager.Stub { final ProtectedPackages mProtectedPackages = new ProtectedPackages(); - boolean mRestoredSettings; + boolean mFirstBoot; // System configuration read by SystemConfig. final int[] mGlobalGids; @@ -1125,204 +1120,7 @@ public class PackageManagerService extends IPackageManager.Stub { final @NonNull String mSharedSystemSharedLibraryPackageName; private final PackageUsage mPackageUsage = new PackageUsage(); - - private class PackageUsage { - private static final int WRITE_INTERVAL - = (DEBUG_DEXOPT) ? 0 : 30*60*1000; // 30m in ms - - private final Object mFileLock = new Object(); - private final AtomicLong mLastWritten = new AtomicLong(0); - private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false); - - private boolean mIsHistoricalPackageUsageAvailable = true; - - boolean isHistoricalPackageUsageAvailable() { - return mIsHistoricalPackageUsageAvailable; - } - - void write(boolean force) { - if (force) { - writeInternal(); - return; - } - if (SystemClock.elapsedRealtime() - mLastWritten.get() < WRITE_INTERVAL - && !DEBUG_DEXOPT) { - return; - } - if (mBackgroundWriteRunning.compareAndSet(false, true)) { - new Thread("PackageUsage_DiskWriter") { - @Override - public void run() { - try { - writeInternal(); - } finally { - mBackgroundWriteRunning.set(false); - } - } - }.start(); - } - } - - private void writeInternal() { - synchronized (mPackages) { - synchronized (mFileLock) { - AtomicFile file = getFile(); - FileOutputStream f = null; - try { - f = file.startWrite(); - BufferedOutputStream out = new BufferedOutputStream(f); - FileUtils.setPermissions(file.getBaseFile().getPath(), - 0640, SYSTEM_UID, PACKAGE_INFO_GID); - StringBuilder sb = new StringBuilder(); - - sb.append(USAGE_FILE_MAGIC_VERSION_1); - sb.append('\n'); - out.write(sb.toString().getBytes(StandardCharsets.US_ASCII)); - - for (PackageParser.Package pkg : mPackages.values()) { - if (pkg.getLatestPackageUseTimeInMills() == 0L) { - continue; - } - sb.setLength(0); - sb.append(pkg.packageName); - for (long usageTimeInMillis : pkg.mLastPackageUsageTimeInMills) { - sb.append(' '); - sb.append(usageTimeInMillis); - } - sb.append('\n'); - out.write(sb.toString().getBytes(StandardCharsets.US_ASCII)); - } - out.flush(); - file.finishWrite(f); - } catch (IOException e) { - if (f != null) { - file.failWrite(f); - } - Log.e(TAG, "Failed to write package usage times", e); - } - } - } - mLastWritten.set(SystemClock.elapsedRealtime()); - } - - void readLP() { - synchronized (mFileLock) { - AtomicFile file = getFile(); - BufferedInputStream in = null; - try { - in = new BufferedInputStream(file.openRead()); - StringBuffer sb = new StringBuffer(); - - String firstLine = readLine(in, sb); - if (firstLine == null) { - // Empty file. Do nothing. - } else if (USAGE_FILE_MAGIC_VERSION_1.equals(firstLine)) { - readVersion1LP(in, sb); - } else { - readVersion0LP(in, sb, firstLine); - } - } catch (FileNotFoundException expected) { - mIsHistoricalPackageUsageAvailable = false; - } catch (IOException e) { - Log.w(TAG, "Failed to read package usage times", e); - } finally { - IoUtils.closeQuietly(in); - } - } - mLastWritten.set(SystemClock.elapsedRealtime()); - } - - private void readVersion0LP(InputStream in, StringBuffer sb, String firstLine) - throws IOException { - // Initial version of the file had no version number and stored one - // package-timestamp pair per line. - // Note that the first line has already been read from the InputStream. - for (String line = firstLine; line != null; line = readLine(in, sb)) { - String[] tokens = line.split(" "); - if (tokens.length != 2) { - throw new IOException("Failed to parse " + line + - " as package-timestamp pair."); - } - - String packageName = tokens[0]; - PackageParser.Package pkg = mPackages.get(packageName); - if (pkg == null) { - continue; - } - - long timestamp = parseAsLong(tokens[1]); - for (int reason = 0; - reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT; - reason++) { - pkg.mLastPackageUsageTimeInMills[reason] = timestamp; - } - } - } - - private void readVersion1LP(InputStream in, StringBuffer sb) throws IOException { - // Version 1 of the file started with the corresponding version - // number and then stored a package name and eight timestamps per line. - String line; - while ((line = readLine(in, sb)) != null) { - String[] tokens = line.split(" "); - if (tokens.length != PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT + 1) { - throw new IOException("Failed to parse " + line + " as a timestamp array."); - } - - String packageName = tokens[0]; - PackageParser.Package pkg = mPackages.get(packageName); - if (pkg == null) { - continue; - } - - for (int reason = 0; - reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT; - reason++) { - pkg.mLastPackageUsageTimeInMills[reason] = parseAsLong(tokens[reason + 1]); - } - } - } - - private long parseAsLong(String token) throws IOException { - try { - return Long.parseLong(token); - } catch (NumberFormatException e) { - throw new IOException("Failed to parse " + token + " as a long.", e); - } - } - - private String readLine(InputStream in, StringBuffer sb) throws IOException { - return readToken(in, sb, '\n'); - } - - private String readToken(InputStream in, StringBuffer sb, char endOfToken) - throws IOException { - sb.setLength(0); - while (true) { - int ch = in.read(); - if (ch == -1) { - if (sb.length() == 0) { - return null; - } - throw new IOException("Unexpected EOF"); - } - if (ch == endOfToken) { - return sb.toString(); - } - sb.append((char)ch); - } - } - - private AtomicFile getFile() { - File dataDir = Environment.getDataDirectory(); - File systemDir = new File(dataDir, "system"); - File fname = new File(systemDir, "package-usage.list"); - return new AtomicFile(fname); - } - - private static final String USAGE_FILE_MAGIC = "PACKAGE_USAGE__VERSION_"; - private static final String USAGE_FILE_MAGIC_VERSION_1 = USAGE_FILE_MAGIC + "1"; - } + private final CompilerStats mCompilerStats = new CompilerStats(); class PackageHandler extends Handler { private boolean mBound = false; @@ -2217,6 +2015,34 @@ public class PackageManagerService extends IPackageManager.Stub { displayManager.getDisplay(Display.DEFAULT_DISPLAY).getMetrics(metrics); } + /** + * Requests that files preopted on a secondary system partition be copied to the data partition + * if possible. Note that the actual copying of the files is accomplished by init for security + * reasons. This simply requests that the copy takes place and awaits confirmation of its + * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy. + */ + private static void requestCopyPreoptedFiles() { + final int WAIT_TIME_MS = 100; + final String CP_PREOPT_PROPERTY = "sys.cppreopt"; + if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) { + SystemProperties.set(CP_PREOPT_PROPERTY, "requested"); + // We will wait for up to 100 seconds. + final long timeEnd = SystemClock.uptimeMillis() + 100 * 1000; + while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) { + try { + Thread.sleep(WAIT_TIME_MS); + } catch (InterruptedException e) { + // Do nothing + } + if (SystemClock.uptimeMillis() > timeEnd) { + SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out"); + Slog.wtf(TAG, "cppreopt did not finish!"); + break; + } + } + } + } + public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, @@ -2318,7 +2144,11 @@ public class PackageManagerService extends IPackageManager.Stub { mFoundPolicyFile = SELinuxMMAC.readInstallPolicy(); - mRestoredSettings = mSettings.readLPw(sUserManager.getUsers(false)); + mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false)); + + if (mFirstBoot) { + requestCopyPreoptedFiles(); + } String customResolverActivity = Resources.getSystem().getString( R.string.config_customResolverActivity); @@ -2669,7 +2499,8 @@ public class PackageManagerService extends IPackageManager.Stub { // Now that we know all the packages we are keeping, // read and update their last usage times. - mPackageUsage.readLP(); + mPackageUsage.read(mPackages); + mCompilerStats.read(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis()); @@ -2695,7 +2526,7 @@ public class PackageManagerService extends IPackageManager.Stub { // If this is the first boot or an update from pre-M, and it is a normal // boot, then we need to initialize the default preferred apps across // all defined users. - if (!onlyCore && (mPromoteSystemApps || !mRestoredSettings)) { + if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) { for (UserInfo user : sUserManager.getUsers(true)) { mSettings.applyDefaultPreferredAppsLPw(this, user.id); applyFactoryDefaultBrowserLPw(user.id); @@ -2761,7 +2592,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - int[] stats = performDexOpt(coreApps, false, + int[] stats = performDexOptUpgrade(coreApps, false, getCompilerFilterForReason(REASON_CORE_APP)); final int elapsedTimeSeconds = @@ -2853,7 +2684,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public boolean isFirstBoot() { - return !mRestoredSettings; + return mFirstBoot; } @Override @@ -7268,7 +7099,7 @@ public class PackageManagerService extends IPackageManager.Stub { } final long startTime = System.nanoTime(); - final int[] stats = performDexOpt(pkgs, mIsPreNUpgrade /* showDialog */, + final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */, getCompilerFilterForReason(causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT)); final int elapsedTimeSeconds = @@ -7287,7 +7118,7 @@ public class PackageManagerService extends IPackageManager.Stub { * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped} * and {@code numberOfPackagesFailed}. */ - private int[] performDexOpt(List<PackageParser.Package> pkgs, boolean showDialog, + private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog, String compilerFilter) { int numberOfPackagesVisited = 0; @@ -7321,6 +7152,19 @@ public class PackageManagerService extends IPackageManager.Stub { } } + // If the OTA updates a system app which was previously preopted to a non-preopted state + // the app might end up being verified at runtime. That's because by default the apps + // are verify-profile but for preopted apps there's no profile. + // Do a hacky check to ensure that if we have no profiles (a reasonable indication + // that before the OTA the app was preopted) the app gets compiled with a non-profile + // filter (by default interpret-only). + // Note that at this stage unused apps are already filtered. + if (isSystemApp(pkg) && + DexFile.isProfileGuidedCompilerFilter(compilerFilter) && + !Environment.getReferenceProfile(pkg.packageName).exists()) { + compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter); + } + // checkProfiles is false to avoid merging profiles during boot which // might interfere with background compilation (b/28612421). // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will @@ -7407,7 +7251,8 @@ public class PackageManagerService extends IPackageManager.Stub { // Package could not be found. Report failure. return PackageDexOptimizer.DEX_OPT_FAILED; } - mPackageUsage.write(false); + mPackageUsage.maybeWriteAsync(mPackages); + mCompilerStats.maybeWriteAsync(); } long callingId = Binder.clearCallingIdentity(); try { @@ -7452,11 +7297,12 @@ public class PackageManagerService extends IPackageManager.Stub { // Currently this will do a full compilation of the library by default. pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets, false /* checkProfiles */, - getCompilerFilterForReason(REASON_NON_SYSTEM_LIBRARY)); + getCompilerFilterForReason(REASON_NON_SYSTEM_LIBRARY), + getOrCreateCompilerPackageStats(depPackage)); } } return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets, checkProfiles, - targetCompilerFilter); + targetCompilerFilter, getOrCreateCompilerPackageStats(p)); } Collection<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) { @@ -7510,7 +7356,8 @@ public class PackageManagerService extends IPackageManager.Stub { } public void shutdown() { - mPackageUsage.write(true); + mPackageUsage.writeNow(mPackages); + mCompilerStats.writeNow(); } @Override @@ -15142,7 +14989,8 @@ public class PackageManagerService extends IPackageManager.Stub { // Also, don't fail application installs if the dexopt step fails. mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles, null /* instructionSets */, false /* checkProfiles */, - getCompilerFilterForReason(REASON_INSTALL)); + getCompilerFilterForReason(REASON_INSTALL), + getOrCreateCompilerPackageStats(pkg)); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); // Notify BackgroundDexOptService that the package has been changed. @@ -18101,6 +17949,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); public static final int DUMP_DOMAIN_PREFERRED = 1 << 18; public static final int DUMP_FROZEN = 1 << 19; public static final int DUMP_DEXOPT = 1 << 20; + public static final int DUMP_COMPILER_STATS = 1 << 21; public static final int OPTION_SHOW_FILTERS = 1 << 0; @@ -18218,6 +18067,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); pw.println(" installs: details about install sessions"); pw.println(" check-permission <permission> <package> [<user>]: does pkg hold perm?"); pw.println(" dexopt: dump dexopt state"); + pw.println(" compiler-stats: dump compiler statistics"); pw.println(" <package.name>: info about given package"); return; } else if ("--checkin".equals(opt)) { @@ -18339,6 +18189,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); dumpState.setDump(DumpState.DUMP_FROZEN); } else if ("dexopt".equals(cmd)) { dumpState.setDump(DumpState.DUMP_DEXOPT); + } else if ("compiler-stats".equals(cmd)) { + dumpState.setDump(DumpState.DUMP_COMPILER_STATS); } else if ("write".equals(cmd)) { synchronized (mPackages) { mSettings.writeLPr(); @@ -18701,6 +18553,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); dumpDexoptStateLPr(pw, packageName); } + if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) { + if (dumpState.onTitlePrinted()) pw.println(); + dumpCompilerStatsLPr(pw, packageName); + } + if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) { if (dumpState.onTitlePrinted()) pw.println(); mSettings.dumpReadMessagesLPr(pw, dumpState); @@ -18765,6 +18622,38 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } } + private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) { + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); + ipw.println(); + ipw.println("Compiler stats:"); + ipw.increaseIndent(); + Collection<PackageParser.Package> packages = null; + if (packageName != null) { + PackageParser.Package targetPackage = mPackages.get(packageName); + if (targetPackage != null) { + packages = Collections.singletonList(targetPackage); + } else { + ipw.println("Unable to find package: " + packageName); + return; + } + } else { + packages = mPackages.values(); + } + + for (PackageParser.Package pkg : packages) { + ipw.println("[" + pkg.packageName + "]"); + ipw.increaseIndent(); + + CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.packageName); + if (stats == null) { + ipw.println("(No recorded stats)"); + } else { + stats.dump(ipw); + } + ipw.decreaseIndent(); + } + } + private String dumpDomainString(String packageName) { List<IntentFilterVerificationInfo> iviList = getIntentFilterVerifications(packageName) .getList(); @@ -20932,4 +20821,20 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); msg.setData(data); mProcessLoggingHandler.sendMessage(msg); } + + public CompilerStats.PackageStats getCompilerPackageStats(String pkgName) { + return mCompilerStats.getPackageStats(pkgName); + } + + public CompilerStats.PackageStats getOrCreateCompilerPackageStats(PackageParser.Package pkg) { + return getOrCreateCompilerPackageStats(pkg.packageName); + } + + public CompilerStats.PackageStats getOrCreateCompilerPackageStats(String pkgName) { + return mCompilerStats.getOrCreatePackageStats(pkgName); + } + + public void deleteCompilerPackageStats(String pkgName) { + mCompilerStats.deletePackageStats(pkgName); + } } diff --git a/services/core/java/com/android/server/pm/PackageUsage.java b/services/core/java/com/android/server/pm/PackageUsage.java new file mode 100644 index 000000000000..ac1f739cd9f8 --- /dev/null +++ b/services/core/java/com/android/server/pm/PackageUsage.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.os.Process.PACKAGE_INFO_GID; +import static android.os.Process.SYSTEM_UID; + +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.os.FileUtils; +import android.util.AtomicFile; +import android.util.Log; + +import libcore.io.IoUtils; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +class PackageUsage extends AbstractStatsBase<Map<String, PackageParser.Package>> { + + private static final String USAGE_FILE_MAGIC = "PACKAGE_USAGE__VERSION_"; + private static final String USAGE_FILE_MAGIC_VERSION_1 = USAGE_FILE_MAGIC + "1"; + + private boolean mIsHistoricalPackageUsageAvailable = true; + + PackageUsage() { + super("package-usage.list", "PackageUsage_DiskWriter", /* lock */ true); + } + + boolean isHistoricalPackageUsageAvailable() { + return mIsHistoricalPackageUsageAvailable; + } + + @Override + protected void writeInternal(Map<String, PackageParser.Package> packages) { + AtomicFile file = getFile(); + FileOutputStream f = null; + try { + f = file.startWrite(); + BufferedOutputStream out = new BufferedOutputStream(f); + FileUtils.setPermissions(file.getBaseFile().getPath(), + 0640, SYSTEM_UID, PACKAGE_INFO_GID); + StringBuilder sb = new StringBuilder(); + + sb.append(USAGE_FILE_MAGIC_VERSION_1); + sb.append('\n'); + out.write(sb.toString().getBytes(StandardCharsets.US_ASCII)); + + for (PackageParser.Package pkg : packages.values()) { + if (pkg.getLatestPackageUseTimeInMills() == 0L) { + continue; + } + sb.setLength(0); + sb.append(pkg.packageName); + for (long usageTimeInMillis : pkg.mLastPackageUsageTimeInMills) { + sb.append(' '); + sb.append(usageTimeInMillis); + } + sb.append('\n'); + out.write(sb.toString().getBytes(StandardCharsets.US_ASCII)); + } + out.flush(); + file.finishWrite(f); + } catch (IOException e) { + if (f != null) { + file.failWrite(f); + } + Log.e(PackageManagerService.TAG, "Failed to write package usage times", e); + } + } + + @Override + protected void readInternal(Map<String, PackageParser.Package> packages) { + AtomicFile file = getFile(); + BufferedInputStream in = null; + try { + in = new BufferedInputStream(file.openRead()); + StringBuffer sb = new StringBuffer(); + + String firstLine = readLine(in, sb); + if (firstLine == null) { + // Empty file. Do nothing. + } else if (USAGE_FILE_MAGIC_VERSION_1.equals(firstLine)) { + readVersion1LP(packages, in, sb); + } else { + readVersion0LP(packages, in, sb, firstLine); + } + } catch (FileNotFoundException expected) { + mIsHistoricalPackageUsageAvailable = false; + } catch (IOException e) { + Log.w(PackageManagerService.TAG, "Failed to read package usage times", e); + } finally { + IoUtils.closeQuietly(in); + } + } + + private void readVersion0LP(Map<String, PackageParser.Package> packages, InputStream in, + StringBuffer sb, String firstLine) + throws IOException { + // Initial version of the file had no version number and stored one + // package-timestamp pair per line. + // Note that the first line has already been read from the InputStream. + for (String line = firstLine; line != null; line = readLine(in, sb)) { + String[] tokens = line.split(" "); + if (tokens.length != 2) { + throw new IOException("Failed to parse " + line + + " as package-timestamp pair."); + } + + String packageName = tokens[0]; + PackageParser.Package pkg = packages.get(packageName); + if (pkg == null) { + continue; + } + + long timestamp = parseAsLong(tokens[1]); + for (int reason = 0; + reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT; + reason++) { + pkg.mLastPackageUsageTimeInMills[reason] = timestamp; + } + } + } + + private void readVersion1LP(Map<String, PackageParser.Package> packages, InputStream in, + StringBuffer sb) throws IOException { + // Version 1 of the file started with the corresponding version + // number and then stored a package name and eight timestamps per line. + String line; + while ((line = readLine(in, sb)) != null) { + String[] tokens = line.split(" "); + if (tokens.length != PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT + 1) { + throw new IOException("Failed to parse " + line + " as a timestamp array."); + } + + String packageName = tokens[0]; + PackageParser.Package pkg = packages.get(packageName); + if (pkg == null) { + continue; + } + + for (int reason = 0; + reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT; + reason++) { + pkg.mLastPackageUsageTimeInMills[reason] = parseAsLong(tokens[reason + 1]); + } + } + } + + private long parseAsLong(String token) throws IOException { + try { + return Long.parseLong(token); + } catch (NumberFormatException e) { + throw new IOException("Failed to parse " + token + " as a long.", e); + } + } + + private String readLine(InputStream in, StringBuffer sb) throws IOException { + return readToken(in, sb, '\n'); + } + + private String readToken(InputStream in, StringBuffer sb, char endOfToken) + throws IOException { + sb.setLength(0); + while (true) { + int ch = in.read(); + if (ch == -1) { + if (sb.length() == 0) { + return null; + } + throw new IOException("Unexpected EOF"); + } + if (ch == endOfToken) { + return sb.toString(); + } + sb.append((char)ch); + } + } +}
\ No newline at end of file diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk index a502c3ac7b1a..ac55a7eec21c 100644 --- a/services/core/jni/Android.mk +++ b/services/core/jni/Android.mk @@ -45,8 +45,6 @@ LOCAL_C_INCLUDES += \ frameworks/base/libs/hwui \ frameworks/base/core/jni \ frameworks/native/services \ - libcore/include \ - libcore/include/libsuspend \ system/security/keystore/include \ $(call include-path-for, libhardware)/hardware \ $(call include-path-for, libhardware_legacy)/hardware_legacy \ diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.h b/services/core/jni/com_android_server_input_InputApplicationHandle.h index 62c85704f61d..e6f25cce1f98 100644 --- a/services/core/jni/com_android_server_input_InputApplicationHandle.h +++ b/services/core/jni/com_android_server_input_InputApplicationHandle.h @@ -26,7 +26,7 @@ namespace android { class NativeInputApplicationHandle : public InputApplicationHandle { public: - NativeInputApplicationHandle(jweak objWeak); + explicit NativeInputApplicationHandle(jweak objWeak); virtual ~NativeInputApplicationHandle(); jobject getInputApplicationHandleObjLocalRef(JNIEnv* env); diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index fc7d7412d8b5..4d7c30440e7d 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -1079,7 +1079,7 @@ public abstract class ConnectionService extends Service { * * @param connectionManagerPhoneAccount See description at * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. - * @param request Details about the incoming call. + * @param request Details about the outgoing call. * @return The {@code Connection} object to satisfy this call, or {@code null} to * not handle the call. */ diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 3674f0f4baa7..706b29495c21 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -133,14 +133,8 @@ interface IWifiManager void setWifiApConfiguration(in WifiConfiguration wifiConfig); - void addToBlacklist(String bssid); - - void clearBlacklist(); - Messenger getWifiServiceMessenger(); - String getConfigFile(); - void enableTdls(String remoteIPAddress, boolean enable); void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable); diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 7c5276c14b76..24cd275fc254 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -1266,6 +1266,7 @@ public class WifiConfiguration implements Parcelable { } mTemporarilyDisabledTimestamp = source.mTemporarilyDisabledTimestamp; mNetworkSelectionBSSID = source.mNetworkSelectionBSSID; + setSeenInLastQualifiedNetworkSelection(source.getSeenInLastQualifiedNetworkSelection()); setCandidate(source.getCandidate()); setCandidateScore(source.getCandidateScore()); setConnectChoice(source.getConnectChoice()); @@ -1406,8 +1407,10 @@ public class WifiConfiguration implements Parcelable { * @hide */ public boolean isEnterprise() { - return allowedKeyManagement.get(KeyMgmt.WPA_EAP) || - allowedKeyManagement.get(KeyMgmt.IEEE8021X); + return (allowedKeyManagement.get(KeyMgmt.WPA_EAP) + || allowedKeyManagement.get(KeyMgmt.IEEE8021X)) + && enterpriseConfig != null + && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE; } @Override diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 08ad35ed2808..c0e8bc200f63 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -33,7 +33,9 @@ import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -116,7 +118,6 @@ public class WifiEnterpriseConfig implements Parcelable { /** @hide */ public static final String CA_CERT_ALIAS_DELIMITER = " "; - // Fields to copy verbatim from wpa_supplicant. private static final String[] SUPPLICANT_CONFIG_KEYS = new String[] { IDENTITY_KEY, @@ -133,6 +134,11 @@ public class WifiEnterpriseConfig implements Parcelable { CA_PATH_KEY }; + /** + * Fields that have unquoted values in {@link #mFields}. + */ + private static final List<String> UNQUOTED_KEYS = Arrays.asList(ENGINE_KEY, OPP_KEY_CACHING); + private HashMap<String, String> mFields = new HashMap<String, String>(); private X509Certificate[] mCaCerts; private PrivateKey mClientPrivateKey; @@ -155,6 +161,9 @@ public class WifiEnterpriseConfig implements Parcelable { for (String key : source.mFields.keySet()) { mFields.put(key, source.mFields.get(key)); } + mCaCerts = source.mCaCerts; + mClientPrivateKey = source.mClientPrivateKey; + mClientCertificate = source.mClientCertificate; mEapMethod = source.mEapMethod; mPhase2Method = source.mPhase2Method; } @@ -455,7 +464,7 @@ public class WifiEnterpriseConfig implements Parcelable { case Eap.AKA: case Eap.AKA_PRIME: mEapMethod = eapMethod; - mFields.put(OPP_KEY_CACHING, "1"); + setFieldValue(OPP_KEY_CACHING, "1"); break; default: throw new IllegalArgumentException("Unknown EAP method"); @@ -514,7 +523,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @return the identity */ public String getIdentity() { - return getFieldValue(IDENTITY_KEY, ""); + return getFieldValue(IDENTITY_KEY); } /** @@ -523,7 +532,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @param anonymousIdentity the anonymous identity */ public void setAnonymousIdentity(String anonymousIdentity) { - setFieldValue(ANON_IDENTITY_KEY, anonymousIdentity, ""); + setFieldValue(ANON_IDENTITY_KEY, anonymousIdentity); } /** @@ -531,7 +540,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @return anonymous identity */ public String getAnonymousIdentity() { - return getFieldValue(ANON_IDENTITY_KEY, ""); + return getFieldValue(ANON_IDENTITY_KEY); } /** @@ -539,7 +548,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @param password the password */ public void setPassword(String password) { - setFieldValue(PASSWORD_KEY, password, ""); + setFieldValue(PASSWORD_KEY, password); } /** @@ -549,7 +558,7 @@ public class WifiEnterpriseConfig implements Parcelable { * framework, returns "*". */ public String getPassword() { - return getFieldValue(PASSWORD_KEY, ""); + return getFieldValue(PASSWORD_KEY); } /** @@ -639,7 +648,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @hide */ @Nullable public String[] getCaCertificateAliases() { - String value = getFieldValue(CA_CERT_KEY, ""); + String value = getFieldValue(CA_CERT_KEY); if (value.startsWith(CA_CERT_PREFIX)) { // Backwards compatibility: parse the original alias prefix. return new String[] {getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX)}; @@ -766,7 +775,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @hide */ public String getCaPath() { - return getFieldValue(CA_PATH_KEY, ""); + return getFieldValue(CA_PATH_KEY); } /** Set Client certificate alias. @@ -782,11 +791,11 @@ public class WifiEnterpriseConfig implements Parcelable { setFieldValue(PRIVATE_KEY_ID_KEY, alias, Credentials.USER_PRIVATE_KEY); // Also, set engine parameters if (TextUtils.isEmpty(alias)) { - mFields.put(ENGINE_KEY, ENGINE_DISABLE); - mFields.put(ENGINE_ID_KEY, EMPTY_VALUE); + setFieldValue(ENGINE_KEY, ENGINE_DISABLE); + setFieldValue(ENGINE_ID_KEY, ""); } else { - mFields.put(ENGINE_KEY, ENGINE_ENABLE); - mFields.put(ENGINE_ID_KEY, convertToQuotedString(ENGINE_ID_KEYSTORE)); + setFieldValue(ENGINE_KEY, ENGINE_ENABLE); + setFieldValue(ENGINE_ID_KEY, ENGINE_ID_KEYSTORE); } } @@ -859,7 +868,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @deprecated in favor of altSubjectMatch */ public void setSubjectMatch(String subjectMatch) { - setFieldValue(SUBJECT_MATCH_KEY, subjectMatch, ""); + setFieldValue(SUBJECT_MATCH_KEY, subjectMatch); } /** @@ -868,7 +877,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @deprecated in favor of altSubjectMatch */ public String getSubjectMatch() { - return getFieldValue(SUBJECT_MATCH_KEY, ""); + return getFieldValue(SUBJECT_MATCH_KEY); } /** @@ -878,7 +887,7 @@ public class WifiEnterpriseConfig implements Parcelable { * DNS:server.example.com;EMAIL:server@example.com */ public void setAltSubjectMatch(String altSubjectMatch) { - setFieldValue(ALTSUBJECT_MATCH_KEY, altSubjectMatch, ""); + setFieldValue(ALTSUBJECT_MATCH_KEY, altSubjectMatch); } /** @@ -886,7 +895,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @return the alternate subject match string */ public String getAltSubjectMatch() { - return getFieldValue(ALTSUBJECT_MATCH_KEY, ""); + return getFieldValue(ALTSUBJECT_MATCH_KEY); } /** @@ -916,7 +925,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @return The domain value. */ public String getDomainSuffixMatch() { - return getFieldValue(DOM_SUFFIX_MATCH_KEY, ""); + return getFieldValue(DOM_SUFFIX_MATCH_KEY); } /** @@ -925,7 +934,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @param realm the realm */ public void setRealm(String realm) { - setFieldValue(REALM_KEY, realm, ""); + setFieldValue(REALM_KEY, realm); } /** @@ -933,7 +942,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @return the realm */ public String getRealm() { - return getFieldValue(REALM_KEY, ""); + return getFieldValue(REALM_KEY); } /** @@ -941,7 +950,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @param plmn the plmn value derived from mcc (mobile country code) & mnc (mobile network code) */ public void setPlmn(String plmn) { - setFieldValue(PLMN_KEY, plmn, ""); + setFieldValue(PLMN_KEY, plmn); } /** @@ -950,7 +959,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @return the plmn */ public String getPlmn() { - return getFieldValue(PLMN_KEY, ""); + return getFieldValue(PLMN_KEY); } /** See {@link WifiConfiguration#getKeyIdForCredentials} @hide */ @@ -995,13 +1004,13 @@ public class WifiEnterpriseConfig implements Parcelable { } /** - * Returns the field value for the key. + * Returns the field value for the key with prefix removed. * @param key into the hash * @param prefix is the prefix that the value may have * @return value * @hide */ - public String getFieldValue(String key, String prefix) { + private String getFieldValue(String key, String prefix) { // TODO: Should raise an exception if |key| is EAP_KEY or PHASE2_KEY since // neither of these keys should be retrieved in this manner. String value = mFields.get(key); @@ -1017,38 +1026,46 @@ public class WifiEnterpriseConfig implements Parcelable { } /** + * Returns the field value for the key. + * @param key into the hash + * @return value + * @hide + */ + public String getFieldValue(String key) { + return getFieldValue(key, ""); + } + + /** * Set a value with an optional prefix at key * @param key into the hash * @param value to be set * @param prefix an optional value to be prefixed to actual value * @hide */ - public void setFieldValue(String key, String value, String prefix) { + private void setFieldValue(String key, String value, String prefix) { // TODO: Should raise an exception if |key| is EAP_KEY or PHASE2_KEY since // neither of these keys should be set in this manner. if (TextUtils.isEmpty(value)) { mFields.put(key, EMPTY_VALUE); } else { - mFields.put(key, convertToQuotedString(prefix + value)); + String valueToSet; + if (!UNQUOTED_KEYS.contains(key)) { + valueToSet = convertToQuotedString(prefix + value); + } else { + valueToSet = prefix + value; + } + mFields.put(key, valueToSet); } } - /** - * Set a value with an optional prefix at key + * Set a value at key * @param key into the hash * @param value to be set - * @param prefix an optional value to be prefixed to actual value * @hide */ public void setFieldValue(String key, String value) { - // TODO: Should raise an exception if |key| is EAP_KEY or PHASE2_KEY since - // neither of these keys should be set in this manner. - if (TextUtils.isEmpty(value)) { - mFields.put(key, EMPTY_VALUE); - } else { - mFields.put(key, convertToQuotedString(value)); - } + setFieldValue(key, value, ""); } @Override diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 19ecbdbd893a..955161cf538b 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1616,41 +1616,6 @@ public class WifiManager { } /** - * Add a bssid to the supplicant blacklist - * - * This API is used by WifiWatchdogService - * - * @return {@code true} if the operation succeeds else {@code false} - * @hide - */ - public boolean addToBlacklist(String bssid) { - try { - mService.addToBlacklist(bssid); - return true; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Clear the supplicant blacklist - * - * This API is used by WifiWatchdogService - * - * @return {@code true} if the operation succeeds else {@code false} - * @hide - */ - public boolean clearBlacklist() { - try { - mService.clearBlacklist(); - return true; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - - /** * Enable/Disable TDLS on a specific local route. * * <p> @@ -2148,18 +2113,6 @@ public class WifiManager { /** - * Returns the file in which IP and proxy configuration data is stored - * @hide - */ - public String getConfigFile() { - try { - return mService.getConfigFile(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Allows an application to keep the Wi-Fi radio awake. * Normally the Wi-Fi radio may turn off when the user has not used the device in a while. * Acquiring a WifiLock will keep the radio on until the lock is released. Multiple diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index 716f1d32ec84..5847f798712d 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -34,9 +34,10 @@ import com.android.internal.util.AsyncChannel; import com.android.internal.util.Preconditions; import com.android.internal.util.Protocol; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; - /** * This class provides a way to scan the Wifi universe around the device * Get an instance of this class by calling @@ -167,18 +168,32 @@ public class WifiScanner { * scan configuration parameters to be sent to {@link #startBackgroundScan} */ public static class ScanSettings implements Parcelable { + /** + * Hidden network to be scanned for. + * {@hide} + */ + public static class HiddenNetwork { + /** SSID of the network */ + public String ssid; + + /** + * Default constructor for HiddenNetwork. + */ + public HiddenNetwork(String ssid) { + this.ssid = ssid; + } + } /** one of the WIFI_BAND values */ public int band; /** list of channels; used when band is set to WIFI_BAND_UNSPECIFIED */ public ChannelSpec[] channels; /** - * list of networkId's of hidden networks to scan for. - * These Id's should correspond to the wpa_supplicant's networkId's and will be used - * in connectivity scans using wpa_supplicant. + * list of hidden networks to scan for. Explicit probe requests are sent out for such + * networks during scan. Only valid for single scan requests. * {@hide} * */ - public int[] hiddenNetworkIds; + public HiddenNetwork[] hiddenNetworks; /** period of background scan; in millisecond, 0 => single shot scan */ public int periodInMs; /** must have a valid REPORT_EVENT value */ @@ -233,7 +248,14 @@ public class WifiScanner { } else { dest.writeInt(0); } - dest.writeIntArray(hiddenNetworkIds); + if (hiddenNetworks != null) { + dest.writeInt(hiddenNetworks.length); + for (int i = 0; i < hiddenNetworks.length; i++) { + dest.writeString(hiddenNetworks[i].ssid); + } + } else { + dest.writeInt(0); + } } /** Implement the Parcelable interface {@hide} */ @@ -258,7 +280,12 @@ public class WifiScanner { spec.passive = in.readInt() == 1; settings.channels[i] = spec; } - settings.hiddenNetworkIds = in.createIntArray(); + int numNetworks = in.readInt(); + settings.hiddenNetworks = new HiddenNetwork[numNetworks]; + for (int i = 0; i < numNetworks; i++) { + String ssid = in.readString(); + settings.hiddenNetworks[i] = new HiddenNetwork(ssid);; + } return settings; } @@ -286,6 +313,12 @@ public class WifiScanner { * {@hide} */ private int mBucketsScanned; + /** + * Indicates that the scan results received are as a result of a scan of all available + * channels. This should only be expected to function for single scans. + * {@hide} + */ + private boolean mAllChannelsScanned; /** all scan results discovered in this scan, sorted by timestamp in ascending order */ private ScanResult mResults[]; @@ -298,10 +331,12 @@ public class WifiScanner { } /** {@hide} */ - public ScanData(int id, int flags, int bucketsScanned, ScanResult[] results) { + public ScanData(int id, int flags, int bucketsScanned, boolean allChannelsScanned, + ScanResult[] results) { mId = id; mFlags = flags; mBucketsScanned = bucketsScanned; + mAllChannelsScanned = allChannelsScanned; mResults = results; } @@ -309,6 +344,7 @@ public class WifiScanner { mId = s.mId; mFlags = s.mFlags; mBucketsScanned = s.mBucketsScanned; + mAllChannelsScanned = s.mAllChannelsScanned; mResults = new ScanResult[s.mResults.length]; for (int i = 0; i < s.mResults.length; i++) { ScanResult result = s.mResults[i]; @@ -330,6 +366,11 @@ public class WifiScanner { return mBucketsScanned; } + /** {@hide} */ + public boolean isAllChannelsScanned() { + return mAllChannelsScanned; + } + public ScanResult[] getResults() { return mResults; } @@ -345,6 +386,7 @@ public class WifiScanner { dest.writeInt(mId); dest.writeInt(mFlags); dest.writeInt(mBucketsScanned); + dest.writeInt(mAllChannelsScanned ? 1 : 0); dest.writeInt(mResults.length); for (int i = 0; i < mResults.length; i++) { ScanResult result = mResults[i]; @@ -362,12 +404,13 @@ public class WifiScanner { int id = in.readInt(); int flags = in.readInt(); int bucketsScanned = in.readInt(); + boolean allChannelsScanned = in.readInt() != 0; int n = in.readInt(); ScanResult results[] = new ScanResult[n]; for (int i = 0; i < n; i++) { results[i] = ScanResult.CREATOR.createFromParcel(in); } - return new ScanData(id, flags, bucketsScanned, results); + return new ScanData(id, flags, bucketsScanned, allChannelsScanned, results); } public ScanData[] newArray(int size) { @@ -520,10 +563,6 @@ public class WifiScanner { /** SSID of the network */ public String ssid; - /** Network ID in wpa_supplicant */ - public int networkId; - /** Assigned priority for the network */ - public int priority; /** Bitmask of the FLAG_XXX */ public byte flags; /** Bitmask of the ATUH_XXX */ @@ -580,8 +619,6 @@ public class WifiScanner { dest.writeInt(networkList.length); for (int i = 0; i < networkList.length; i++) { dest.writeString(networkList[i].ssid); - dest.writeInt(networkList[i].networkId); - dest.writeInt(networkList[i].priority); dest.writeByte(networkList[i].flags); dest.writeByte(networkList[i].authBitField); } @@ -608,8 +645,6 @@ public class WifiScanner { for (int i = 0; i < numNetworks; i++) { String ssid = in.readString(); PnoNetwork network = new PnoNetwork(ssid); - network.networkId = in.readInt(); - network.priority = in.readInt(); network.flags = in.readByte(); network.authBitField = in.readByte(); settings.networkList[i] = network; @@ -788,6 +823,22 @@ public class WifiScanner { mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key); } + /** + * Retrieve the most recent scan results from a single scan request. + * {@hide} + */ + public List<ScanResult> getSingleScanResults() { + validateChannel(); + Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0); + if (reply.what == WifiScanner.CMD_OP_SUCCEEDED) { + return Arrays.asList(((ParcelableScanResults) reply.obj).getResults()); + } + OperationResult result = (OperationResult) reply.obj; + Log.e(TAG, "Error retrieving SingleScan results reason: " + result.reason + + " description: " + result.description); + return new ArrayList<ScanResult>(); + } + private void startPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, int key) { // Bundle up both the settings and send it across. Bundle pnoParams = new Bundle(); @@ -1167,6 +1218,8 @@ public class WifiScanner { public static final int CMD_REGISTER_SCAN_LISTENER = BASE + 27; /** @hide */ public static final int CMD_DEREGISTER_SCAN_LISTENER = BASE + 28; + /** @hide */ + public static final int CMD_GET_SINGLE_SCAN_RESULTS = BASE + 29; private Context mContext; private IWifiScanner mService; diff --git a/wifi/java/android/net/wifi/nan/ConfigRequest.java b/wifi/java/android/net/wifi/nan/ConfigRequest.java index 44544de0e9fd..78e40526ba5b 100644 --- a/wifi/java/android/net/wifi/nan/ConfigRequest.java +++ b/wifi/java/android/net/wifi/nan/ConfigRequest.java @@ -23,14 +23,14 @@ import android.os.Parcelable; /** * Defines a request object to configure a Wi-Fi NAN network. Built using * {@link ConfigRequest.Builder}. Configuration is requested using - * {@link WifiNanManager#connect(android.os.Looper, ConfigRequest, WifiNanEventCallback)}. + * {@link WifiNanManager#connect(android.os.Handler, ConfigRequest, WifiNanEventCallback)}. * Note that the actual achieved configuration may be different from the * requested configuration - since different applications may request different * configurations. * * @hide PROPOSED_NAN_API */ -public class ConfigRequest implements Parcelable { +public final class ConfigRequest implements Parcelable { /** * Lower range of possible cluster ID. * diff --git a/wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl b/wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl index ff2c409c1dd5..f2e371dd74f9 100644 --- a/wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl +++ b/wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl @@ -21,9 +21,9 @@ package android.net.wifi.nan; * * {@hide} */ -oneway interface IWifiNanSessionCallback +oneway interface IWifiNanDiscoverySessionCallback { - void onSessionStarted(int sessionId); + void onSessionStarted(int discoverySessionId); void onSessionConfigSuccess(); void onSessionConfigFail(int reason); void onSessionTerminated(int reason); diff --git a/wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl b/wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl index a4e590beca59..9ac7bf24e18e 100644 --- a/wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl +++ b/wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl @@ -26,7 +26,7 @@ import android.net.wifi.RttManager; */ oneway interface IWifiNanEventCallback { - void onConnectSuccess(); + void onConnectSuccess(int clientId); void onConnectFail(int reason); void onIdentityChanged(in byte[] mac); diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl index 17ec1bc240e0..4e1f607e0902 100644 --- a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl +++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl @@ -19,8 +19,8 @@ package android.net.wifi.nan; import android.app.PendingIntent; import android.net.wifi.nan.ConfigRequest; +import android.net.wifi.nan.IWifiNanDiscoverySessionCallback; import android.net.wifi.nan.IWifiNanEventCallback; -import android.net.wifi.nan.IWifiNanSessionCallback; import android.net.wifi.nan.PublishConfig; import android.net.wifi.nan.SubscribeConfig; import android.net.wifi.RttManager; @@ -38,19 +38,20 @@ interface IWifiNanManager boolean isUsageEnabled(); // client API - int connect(in IBinder binder, in String callingPackage, in IWifiNanEventCallback callback, + void connect(in IBinder binder, in String callingPackage, in IWifiNanEventCallback callback, in ConfigRequest configRequest); void disconnect(int clientId, in IBinder binder); - void publish(int clientId, in PublishConfig publishConfig, in IWifiNanSessionCallback callback); + void publish(int clientId, in PublishConfig publishConfig, + in IWifiNanDiscoverySessionCallback callback); void subscribe(int clientId, in SubscribeConfig subscribeConfig, - in IWifiNanSessionCallback callback); + in IWifiNanDiscoverySessionCallback callback); // session API - void updatePublish(int clientId, int sessionId, in PublishConfig publishConfig); - void updateSubscribe(int clientId, int sessionId, in SubscribeConfig subscribeConfig); - void sendMessage(int clientId, int sessionId, int peerId, in byte[] message, int messageId, + void updatePublish(int clientId, int discoverySessionId, in PublishConfig publishConfig); + void updateSubscribe(int clientId, int discoverySessionId, in SubscribeConfig subscribeConfig); + void sendMessage(int clientId, int discoverySessionId, int peerId, in byte[] message, int messageId, int retryCount); - void terminateSession(int clientId, int sessionId); - int startRanging(int clientId, int sessionId, in RttManager.ParcelableRttParams parms); + void terminateSession(int clientId, int discoverySessionId); + int startRanging(int clientId, int discoverySessionId, in RttManager.ParcelableRttParams parms); } diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.java b/wifi/java/android/net/wifi/nan/PublishConfig.java index 71f99d98a7bc..f988c0b03ca5 100644 --- a/wifi/java/android/net/wifi/nan/PublishConfig.java +++ b/wifi/java/android/net/wifi/nan/PublishConfig.java @@ -32,12 +32,12 @@ import java.util.Arrays; /** * Defines the configuration of a NAN publish session. Built using * {@link PublishConfig.Builder}. A publish session is created using - * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} or updated using - * {@link WifiNanPublishSession#updatePublish(PublishConfig)}. + * {@link WifiNanManager#publish(PublishConfig, WifiNanDiscoverySessionCallback)} or updated using + * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)}. * * @hide PROPOSED_NAN_API */ -public class PublishConfig implements Parcelable { +public final class PublishConfig implements Parcelable { /** @hide */ @IntDef({ PUBLISH_TYPE_UNSOLICITED, PUBLISH_TYPE_SOLICITED }) @@ -318,12 +318,13 @@ public class PublishConfig implements Parcelable { * Sets the number of times an unsolicited (configured using * {@link PublishConfig.Builder#setPublishType(int)}) publish session * will be broadcast. When the count is reached an event will be - * generated for {@link WifiNanSessionCallback#onSessionTerminated(int)} - * with {@link WifiNanSessionCallback#TERMINATE_REASON_DONE} [unless + * generated for {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} + * with {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE} [unless * {@link #setEnableTerminateNotification(boolean)} disables the callback]. * <p> * Optional. 0 by default - indicating the session doesn't terminate on its own. - * Session will be terminated when {@link WifiNanSession#terminate()} is called. + * Session will be terminated when {@link WifiNanDiscoveryBaseSession#terminate()} is + * called. * * @param publishCount Number of publish packets to broadcast. * @@ -343,12 +344,13 @@ public class PublishConfig implements Parcelable { * {@link PublishConfig.Builder#setPublishType(int)}) publish session * will be alive - broadcasting a packet. When the TTL is reached * an event will be generated for - * {@link WifiNanSessionCallback#onSessionTerminated(int)} with - * {@link WifiNanSessionCallback#TERMINATE_REASON_DONE} [unless + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} with + * {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE} [unless * {@link #setEnableTerminateNotification(boolean)} disables the callback]. * <p> * Optional. 0 by default - indicating the session doesn't terminate on its own. - * Session will be terminated when {@link WifiNanSession#terminate()} is called. + * Session will be terminated when {@link WifiNanDiscoveryBaseSession#terminate()} is + * called. * * @param ttlSec Lifetime of a publish session in seconds. * @@ -365,7 +367,7 @@ public class PublishConfig implements Parcelable { /** * Configure whether a publish terminate notification - * {@link WifiNanSessionCallback#onSessionTerminated(int)} is reported + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} is reported * back to the callback. * * @param enable If true the terminate callback will be called when the diff --git a/wifi/java/android/net/wifi/nan/SubscribeConfig.java b/wifi/java/android/net/wifi/nan/SubscribeConfig.java index 7904875a4064..47f9398b642f 100644 --- a/wifi/java/android/net/wifi/nan/SubscribeConfig.java +++ b/wifi/java/android/net/wifi/nan/SubscribeConfig.java @@ -32,12 +32,12 @@ import java.util.Arrays; /** * Defines the configuration of a NAN subscribe session. Built using * {@link SubscribeConfig.Builder}. Subscribe is done using - * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} or - * {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}. + * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)} or + * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. * * @hide PROPOSED_NAN_API */ -public class SubscribeConfig implements Parcelable { +public final class SubscribeConfig implements Parcelable { /** @hide */ @IntDef({ SUBSCRIBE_TYPE_PASSIVE, SUBSCRIBE_TYPE_ACTIVE }) @@ -350,11 +350,12 @@ public class SubscribeConfig implements Parcelable { * Sets the number of times an active ( * {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session * will broadcast. When the count is reached an event will be - * generated for {@link WifiNanSessionCallback#onSessionTerminated(int)} - * with {@link WifiNanSessionCallback#TERMINATE_REASON_DONE}. + * generated for {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} + * with {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE}. * <p> * Optional. 0 by default - indicating the session doesn't terminate on its own. - * Session will be terminated when {@link WifiNanSession#terminate()} is called. + * Session will be terminated when {@link WifiNanDiscoveryBaseSession#terminate()} is + * called. * * @param subscribeCount Number of subscribe packets to broadcast. * @@ -374,11 +375,12 @@ public class SubscribeConfig implements Parcelable { * {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session * will be alive - i.e. broadcasting a packet. When the TTL is reached * an event will be generated for - * {@link WifiNanSessionCallback#onSessionTerminated(int)} with - * {@link WifiNanSessionCallback#TERMINATE_REASON_DONE}. + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} with + * {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE}. * <p> * Optional. 0 by default - indicating the session doesn't terminate on its own. - * Session will be terminated when {@link WifiNanSession#terminate()} is called. + * Session will be terminated when {@link WifiNanDiscoveryBaseSession#terminate()} is + * called. * * @param ttlSec Lifetime of a subscribe session in seconds. * @@ -397,7 +399,7 @@ public class SubscribeConfig implements Parcelable { * Sets the match style of the subscription - how are matches from a * single match session (corresponding to the same publish action on the * peer) reported to the host (using the - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} * ). The options are: only report the first match and ignore the rest * {@link SubscribeConfig#MATCH_STYLE_FIRST_ONLY} or report every single * match {@link SubscribeConfig#MATCH_STYLE_ALL} (the default). @@ -417,7 +419,7 @@ public class SubscribeConfig implements Parcelable { /** * Configure whether a subscribe terminate notification - * {@link WifiNanSessionCallback#onSessionTerminated(int)} is reported + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} is reported * back to the callback. * * @param enable If true the terminate callback will be called when the diff --git a/wifi/java/android/net/wifi/nan/WifiNanDiscoveryBaseSession.java b/wifi/java/android/net/wifi/nan/WifiNanDiscoveryBaseSession.java new file mode 100644 index 000000000000..c2f373595ff5 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/WifiNanDiscoveryBaseSession.java @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.nan; + +import android.annotation.Nullable; +import android.net.wifi.RttManager; +import android.util.Log; + +import dalvik.system.CloseGuard; + +import java.lang.ref.WeakReference; + +/** + * A class representing a single publish or subscribe NAN session. This object + * will not be created directly - only its child classes are available: + * {@link WifiNanPublishDiscoverySession} and {@link WifiNanSubscribeDiscoverySession}. This + * class provides functionality common to both publish and subscribe discovery sessions: + * <ul> + * <li>Sending messages: {@link #sendMessage(int, byte[], int)} or + * {@link #sendMessage(int, byte[], int, int)} methods. + * <li>Creating a network-specifier when requesting a NAN connection: + * {@link #createNetworkSpecifier(int, int, byte[])}. + * </ul> + * The {@link #terminate()} method must be called to terminate discovery sessions once they are + * no longer needed. + * + * @hide PROPOSED_NAN_API + */ +public class WifiNanDiscoveryBaseSession { + private static final String TAG = "WifiNanDiscoveryBaseSsn"; + private static final boolean DBG = false; + private static final boolean VDBG = false; // STOPSHIP if true + + private static final int MAX_SEND_RETRY_COUNT = 5; + + /** @hide */ + protected WeakReference<WifiNanManager> mMgr; + /** @hide */ + protected final int mClientId; + /** @hide */ + protected final int mSessionId; + /** @hide */ + protected boolean mTerminated = false; + + private final CloseGuard mCloseGuard = CloseGuard.get(); + + /** + * Return the maximum permitted retry count when sending messages using + * {@link #sendMessage(int, byte[], int, int)}. + * + * @return Maximum retry count when sending messages. + */ + public static int getMaxSendRetryCount() { + return MAX_SEND_RETRY_COUNT; + } + + /** @hide */ + public WifiNanDiscoveryBaseSession(WifiNanManager manager, int clientId, int sessionId) { + if (VDBG) { + Log.v(TAG, "New discovery session created: manager=" + manager + ", clientId=" + + clientId + ", sessionId=" + sessionId); + } + + mMgr = new WeakReference<>(manager); + mClientId = clientId; + mSessionId = sessionId; + + mCloseGuard.open("terminate"); + } + + /** + * Terminate the publish or subscribe session - free any resources, and stop + * transmitting packets on-air (for an active session) or listening for + * matches (for a passive session). The session may not be used for any + * additional operations after termination. + * <p> + * This operation must be done on a session which is no longer needed. Otherwise system + * resources will continue to be utilized until the application terminates. The only + * exception is a session for which we received a termination callback, + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)}. + */ + public void terminate() { + WifiNanManager mgr = mMgr.get(); + if (mgr == null) { + Log.w(TAG, "terminate: called post GC on WifiNanManager"); + return; + } + mgr.terminateSession(mClientId, mSessionId); + mTerminated = true; + mMgr.clear(); + mCloseGuard.close(); + } + + /** + * Sets the status of the session to terminated - i.e. an indication that + * already terminated rather than executing a termination. + * + * @hide + */ + public void setTerminated() { + if (mTerminated) { + Log.w(TAG, "terminate: already terminated."); + return; + } + mTerminated = true; + mMgr.clear(); + mCloseGuard.close(); + } + + /** @hide */ + @Override + protected void finalize() throws Throwable { + try { + if (!mTerminated) { + mCloseGuard.warnIfOpen(); + terminate(); + } + } finally { + super.finalize(); + } + } + + /** + * Sends a message to the specified destination. NAN messages are transmitted in the context + * of a discovery session - executed subsequent to a publish/subscribe + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} event. + * <p> + * NAN messages are not guaranteed delivery. Callbacks on + * {@link WifiNanDiscoverySessionCallback} indicate message was transmitted successfully, + * {@link WifiNanDiscoverySessionCallback#onMessageSendSuccess(int)}, or transmission failed + * (possibly after several retries) - + * {@link WifiNanDiscoverySessionCallback#onMessageSendFail(int, int)}. + * <p> + * The peer will get a callback indicating a message was received using + * {@link WifiNanDiscoverySessionCallback#onMessageReceived(int, byte[])}. + * + * @param peerId The peer's ID for the message. Must be a result of an + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} or + * {@link WifiNanDiscoverySessionCallback#onMessageReceived(int, byte[])} events. + * @param message The message to be transmitted. + * @param messageId An arbitrary integer used by the caller to identify the message. The same + * integer ID will be returned in the callbacks indicating message send success or + * failure. The {@code messageId} is not used internally by the NAN service - it + * can be arbitrary and non-unique. + * @param retryCount An integer specifying how many additional service-level (as opposed to PHY + * or MAC level) retries should be attempted if there is no ACK from the receiver + * (note: no retransmissions are attempted in other failure cases). A value of 0 + * indicates no retries. Max permitted value is {@link #getMaxSendRetryCount()}. + */ + public void sendMessage(int peerId, @Nullable byte[] message, int messageId, int retryCount) { + if (mTerminated) { + Log.w(TAG, "sendMessage: called on terminated session"); + return; + } else { + WifiNanManager mgr = mMgr.get(); + if (mgr == null) { + Log.w(TAG, "sendMessage: called post GC on WifiNanManager"); + return; + } + + mgr.sendMessage(mClientId, mSessionId, peerId, message, messageId, retryCount); + } + } + + /** + * Sends a message to the specified destination. NAN messages are transmitted in the context + * of a discovery session - executed subsequent to a publish/subscribe + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} event. + * <p> + * NAN messages are not guaranteed delivery. Callbacks on + * {@link WifiNanDiscoverySessionCallback} indicate message was transmitted successfully, + * {@link WifiNanDiscoverySessionCallback#onMessageSendSuccess(int)}, or transmission failed + * (possibly after several retries) - + * {@link WifiNanDiscoverySessionCallback#onMessageSendFail(int, int)}. + * <p> + * The peer will get a callback indicating a message was received using + * {@link WifiNanDiscoverySessionCallback#onMessageReceived(int, byte[])}. + * Equivalent to {@link #sendMessage(int, byte[], int, int)} with a {@code retryCount} of + * 0. + * + * @param peerId The peer's ID for the message. Must be a result of an + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} or + * {@link WifiNanDiscoverySessionCallback#onMessageReceived(int, byte[])} events. + * @param message The message to be transmitted. + * @param messageId An arbitrary integer used by the caller to identify the message. The same + * integer ID will be returned in the callbacks indicating message send success or + * failure. The {@code messageId} is not used internally by the NAN service - it + * can be arbitrary and non-unique. + */ + public void sendMessage(int peerId, @Nullable byte[] message, int messageId) { + sendMessage(peerId, message, messageId, 0); + } + + /** + * Start a ranging operation with the specified peers. The peer IDs are obtained from an + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} or + * {@link WifiNanDiscoverySessionCallback#onMessageReceived(int, byte[])} operation - can only + * range devices which are part of an ongoing discovery session. + * + * @param params RTT parameters - each corresponding to a specific peer ID (the array sizes + * must be identical). The + * {@link android.net.wifi.RttManager.RttParams#bssid} member must be set to + * a peer ID - not to a MAC address. + * @param listener The listener to receive the results of the ranging session. + * @hide PROPOSED_NAN_SYSTEM_API [TODO: b/28847998 - track RTT API & visilibity] + */ + public void startRanging(RttManager.RttParams[] params, RttManager.RttListener listener) { + if (mTerminated) { + Log.w(TAG, "startRanging: called on terminated session"); + return; + } else { + WifiNanManager mgr = mMgr.get(); + if (mgr == null) { + Log.w(TAG, "startRanging: called post GC on WifiNanManager"); + return; + } + + mgr.startRanging(mClientId, mSessionId, params, listener); + } + } + + /** + * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a + * WiFi NAN connection to the specified peer. The + * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to + * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}. + * <p> + * This method should be used when setting up a connection with a peer discovered through NAN + * discovery or communication (in such scenarios the MAC address of the peer is shielded by + * an opaque peer ID handle). If a NAN connection is needed to a peer discovered using other + * OOB (out-of-band) mechanism then use the alternative + * {@link WifiNanSession#createNetworkSpecifier(int, byte[], byte[])} method - which uses the + * peer's MAC address. + * + * @param role The role of this device: + * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_INITIATOR} or + * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_RESPONDER} + * @param peerId The peer ID obtained through + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} or + * {@link WifiNanDiscoverySessionCallback#onMessageReceived(int, byte[])}. On a RESPONDER this + * value is used to gate the acceptance of a connection request from only that + * peer. A RESPONDER may specified a 0 - indicating that it will accept + * connection requests from any device. + * @param token An arbitrary token (message) to be used to match connection initiation request + * to a responder setup. A RESPONDER is set up with a {@code token} which must + * be matched by the token provided by the INITIATOR. A null token is permitted + * on the RESPONDER and matches any peer token. An empty ({@code ""}) token is + * not the same as a null token and requires the peer token to be empty as well. + * + * @return A string to be used to construct + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,android.net.ConnectivityManager.NetworkCallback)} + * [or other varieties of that API]. + */ + public String createNetworkSpecifier(@WifiNanManager.DataPathRole int role, int peerId, + @Nullable byte[] token) { + if (mTerminated) { + Log.w(TAG, "createNetworkSpecifier: called on terminated session"); + return null; + } else { + WifiNanManager mgr = mMgr.get(); + if (mgr == null) { + Log.w(TAG, "createNetworkSpecifier: called post GC on WifiNanManager"); + return null; + } + + return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerId, token); + } + } +} diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionCallback.java b/wifi/java/android/net/wifi/nan/WifiNanDiscoverySessionCallback.java index 8433b99315ce..685e1524a27a 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanSessionCallback.java +++ b/wifi/java/android/net/wifi/nan/WifiNanDiscoverySessionCallback.java @@ -26,14 +26,14 @@ import java.lang.annotation.RetentionPolicy; * Base class for NAN session events callbacks. Should be extended by * applications wanting notifications. The callbacks are set when a * publish or subscribe session is created using - * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} or - * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} . + * {@link WifiNanManager#publish(PublishConfig, WifiNanDiscoverySessionCallback)} or + * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)} . * <p> * A single callback is set at session creation - it cannot be replaced. * * @hide PROPOSED_NAN_API */ -public class WifiNanSessionCallback { +public class WifiNanDiscoverySessionCallback { /** @hide */ @IntDef({ REASON_NO_RESOURCES, REASON_INVALID_ARGS, REASON_NO_MATCH_SESSION, @@ -51,20 +51,20 @@ public class WifiNanSessionCallback { /** * Indicates no resources to execute the requested operation. - * Failure reason flag for {@link WifiNanSessionCallback} callbacks. + * Failure reason flag for {@link WifiNanDiscoverySessionCallback} callbacks. */ public static final int REASON_NO_RESOURCES = 0; /** * Indicates invalid argument in the requested operation. - * Failure reason flag for {@link WifiNanSessionCallback} callbacks. + * Failure reason flag for {@link WifiNanDiscoverySessionCallback} callbacks. */ public static final int REASON_INVALID_ARGS = 1; /** * Indicates a message is transmitted without a match (a discovery) or received message * from peer occurring first. - * Failure reason flag for {@link WifiNanSessionCallback} callbacks. + * Failure reason flag for {@link WifiNanDiscoverySessionCallback} callbacks. */ public static final int REASON_NO_MATCH_SESSION = 2; @@ -72,13 +72,13 @@ public class WifiNanSessionCallback { * Indicates transmission failure: this may be due to local transmission * failure or to no ACK received - remote device didn't receive the * sent message. Failure reason flag for - * {@link WifiNanSessionCallback#onMessageSendFail(int, int)} callback. + * {@link WifiNanDiscoverySessionCallback#onMessageSendFail(int, int)} callback. */ public static final int REASON_TX_FAIL = 3; /** * Indicates an unspecified error occurred during the operation. - * Failure reason flag for {@link WifiNanSessionCallback} callbacks. + * Failure reason flag for {@link WifiNanDiscoverySessionCallback} callbacks. */ public static final int REASON_OTHER = 4; @@ -86,7 +86,7 @@ public class WifiNanSessionCallback { * Indicates that publish or subscribe session is done - all the * requested operations (per {@link PublishConfig} or * {@link SubscribeConfig}) have been executed. Failure reason flag for - * {@link WifiNanSessionCallback#onSessionTerminated(int)} callback. + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} callback. */ public static final int TERMINATE_REASON_DONE = 100; @@ -94,36 +94,37 @@ public class WifiNanSessionCallback { * Indicates that publish or subscribe session is terminated due to a * failure. * Failure reason flag for - * {@link WifiNanSessionCallback#onSessionTerminated(int)} callback. + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} callback. */ public static final int TERMINATE_REASON_FAIL = 101; /** * Called when a publish operation is started successfully in response to a - * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} operation. + * {@link WifiNanManager#publish(PublishConfig, WifiNanDiscoverySessionCallback)} operation. * - * @param session The {@link WifiNanPublishSession} used to control the + * @param session The {@link WifiNanPublishDiscoverySession} used to control the * discovery session. */ - public void onPublishStarted(@NonNull WifiNanPublishSession session) { + public void onPublishStarted(@NonNull WifiNanPublishDiscoverySession session) { /* empty */ } /** * Called when a subscribe operation is started successfully in response to a - * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} operation. + * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)} operation. * - * @param session The {@link WifiNanSubscribeSession} used to control the + * @param session The {@link WifiNanSubscribeDiscoverySession} used to control the * discovery session. */ - public void onSubscribeStarted(@NonNull WifiNanSubscribeSession session) { + public void onSubscribeStarted(@NonNull WifiNanSubscribeDiscoverySession session) { /* empty */ } /** * Called when a publish or subscribe discovery session configuration is update request - * succeeds. Called in response to {@link WifiNanPublishSession#updatePublish(PublishConfig)} - * or {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}. + * succeeds. Called in response to + * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)} or + * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. */ public void onSessionConfigSuccess() { /* empty */ @@ -131,17 +132,17 @@ public class WifiNanSessionCallback { /** * Called when a publish or subscribe discovery session cannot be created: - * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} or - * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)}, + * {@link WifiNanManager#publish(PublishConfig, WifiNanDiscoverySessionCallback)} or + * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)}, * or when a configuration update fails: - * {@link WifiNanPublishSession#updatePublish(PublishConfig)} or - * {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}. + * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)} or + * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. * <p> * For discovery session updates failure leaves the session running with its previous * configuration - the discovery session is not terminated. * * @param reason The failure reason using - * {@code WifiNanSessionCallback.REASON_*} codes. + * {@code WifiNanDiscoverySessionCallback.REASON_*} codes. */ public void onSessionConfigFail(@SessionReasonCodes int reason) { /* empty */ @@ -149,12 +150,12 @@ public class WifiNanSessionCallback { /** * Called when a discovery session (publish or subscribe) terminates. Termination may be due - * to user-request (either directly through {@link WifiNanSession#terminate()} or + * to user-request (either directly through {@link WifiNanDiscoveryBaseSession#terminate()} or * application-specified expiration, e.g. {@link PublishConfig.Builder#setPublishCount(int)} * or {@link SubscribeConfig.Builder#setTtlSec(int)}) or due to a failure. * * @param reason The termination reason using - * {@code WifiNanSessionCallback.TERMINATE_*} codes. + * {@code WifiNanDiscoverySessionCallback.TERMINATE_*} codes. */ public void onSessionTerminated(@SessionTerminateCodes int reason) { /* empty */ @@ -176,12 +177,12 @@ public class WifiNanSessionCallback { } /** - * Called in response to {@link WifiNanSession#sendMessage(int, byte[], int)} when a message - * is transmitted successfully - when it was received successfully by the peer + * Called in response to {@link WifiNanDiscoveryBaseSession#sendMessage(int, byte[], int)} + * when a message is transmitted successfully - when it was received successfully by the peer * (corresponds to an ACK being received). * <p> * Note that either this callback or - * {@link WifiNanSessionCallback#onMessageSendFail(int, int)} will be + * {@link WifiNanDiscoverySessionCallback#onMessageSendFail(int, int)} will be * received - never both. * * @param messageId The arbitrary message ID specified when sending the message. @@ -193,16 +194,16 @@ public class WifiNanSessionCallback { /** * Called when message transmission fails - when no ACK is received from the peer. * Retries when ACKs are not received are done by hardware, MAC, and in the NAN stack (using - * the {@link WifiNanSession#sendMessage(int, byte[], int, int)} method) - this + * the {@link WifiNanDiscoveryBaseSession#sendMessage(int, byte[], int, int)} method) - this * event is received after all retries are exhausted. * <p> * Note that either this callback or - * {@link WifiNanSessionCallback#onMessageSendSuccess(int)} will be received + * {@link WifiNanDiscoverySessionCallback#onMessageSendSuccess(int)} will be received * - never both. * * @param messageId The arbitrary message ID specified when sending the message. * @param reason The failure reason using - * {@code WifiNanSessionCallback.REASON_*} codes. + * {@code WifiNanDiscoverySessionCallback.REASON_*} codes. */ public void onMessageSendFail(@SuppressWarnings("unused") int messageId, @SessionReasonCodes int reason) { @@ -211,8 +212,8 @@ public class WifiNanSessionCallback { /** * Called when a message is received from a discovery session peer - in response to the - * peer's {@link WifiNanSession#sendMessage(int, byte[], int)} or - * {@link WifiNanSession#sendMessage(int, byte[], int, int)}. + * peer's {@link WifiNanDiscoveryBaseSession#sendMessage(int, byte[], int)} or + * {@link WifiNanDiscoveryBaseSession#sendMessage(int, byte[], int, int)}. * * @param peerId The ID of the peer sending the message. * @param message A byte array containing the message. diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java b/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java index 6e714f14ee63..28c5ca29ea43 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java +++ b/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java @@ -23,9 +23,9 @@ import java.lang.annotation.RetentionPolicy; /** * Base class for NAN events callbacks. Should be extended by applications and set when calling - * {@link WifiNanManager#connect(android.os.Looper, WifiNanEventCallback)}. These are callbacks + * {@link WifiNanManager#connect(android.os.Handler, WifiNanEventCallback)}. These are callbacks * applying to the NAN connection as a whole - not to specific publish or subscribe sessions - - * for that see {@link WifiNanSessionCallback}. + * for that see {@link WifiNanDiscoverySessionCallback}. * * @hide PROPOSED_NAN_API */ @@ -46,7 +46,7 @@ public class WifiNanEventCallback { /** * Indicates that a {@link ConfigRequest} passed in - * {@link WifiNanManager#connect(android.os.Looper, ConfigRequest, WifiNanEventCallback)} + * {@link WifiNanManager#connect(android.os.Handler, ConfigRequest, WifiNanEventCallback)} * couldn't be applied since other connections already exist with an incompatible * configurations. Failure reason flag for {@link WifiNanEventCallback#onConnectFail(int)}. */ @@ -60,16 +60,19 @@ public class WifiNanEventCallback { /** * Called when NAN connect operation - * {@link WifiNanManager#connect(android.os.Looper, WifiNanEventCallback)} + * {@link WifiNanManager#connect(android.os.Handler, WifiNanEventCallback)} * is completed and that we can now start discovery sessions or connections. + * + * @param session The NAN connection on which we can execute further NAN operations - e.g. + * discovery, connections. */ - public void onConnectSuccess() { + public void onConnectSuccess(WifiNanSession session) { /* empty */ } /** * Called when NAN connect operation - * {@link WifiNanManager#connect(android.os.Looper, WifiNanEventCallback)} failed. + * {@link WifiNanManager#connect(android.os.Handler, WifiNanEventCallback)} failed. * * @param reason Failure reason code, see * {@code WifiNanEventCallback.REASON_*}. @@ -91,7 +94,7 @@ public class WifiNanEventCallback { * <p> * This callback is only called if the NAN connection enables it using * {@link ConfigRequest.Builder#setEnableIdentityChangeCallback(boolean)} in - * {@link WifiNanManager#connect(android.os.Looper, ConfigRequest, WifiNanEventCallback)} + * {@link WifiNanManager#connect(android.os.Handler, ConfigRequest, WifiNanEventCallback)} * . It is disabled by default since it may result in additional wake-ups of the host - * increasing power. * diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java index 82d22bce4328..b300d81db4cb 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanManager.java +++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java @@ -21,7 +21,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SystemApi; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkRequest; @@ -29,7 +28,6 @@ import android.net.wifi.RttManager; import android.os.Binder; import android.os.Bundle; import android.os.Handler; -import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; @@ -39,8 +37,6 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; -import dalvik.system.CloseGuard; - import libcore.util.HexEncoding; import org.json.JSONException; @@ -60,15 +56,15 @@ import java.util.Arrays; * The class provides access to: * <ul> * <li>Initialize a NAN cluster (peer-to-peer synchronization). Refer to - * {@link #connect(Looper, WifiNanEventCallback)}. - * <li>Create discovery sessions (publish or subscribe sessions). - * Refer to {@link #publish(PublishConfig, WifiNanSessionCallback)} and - * {@link #subscribe(SubscribeConfig, WifiNanSessionCallback)}. - * <li>Create a NAN network specifier to be used with + * {@link #connect(Handler, WifiNanEventCallback)}. <li>Create discovery sessions (publish or + * subscribe sessions). Refer to + * {@link WifiNanSession#publish(PublishConfig, WifiNanDiscoverySessionCallback)} and + * {@link WifiNanSession#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)}. <li>Create + * a NAN network specifier to be used with * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)} * to set-up a NAN connection with a peer. Refer to - * {@link WifiNanSession#createNetworkSpecifier(int, int, byte[])} and - * {@link #createNetworkSpecifier(int, byte[], byte[])}. + * {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[])} and + * {@link WifiNanSession#createNetworkSpecifier(int, byte[], byte[])}. * </ul> * <p> * NAN may not be usable when Wi-Fi is disabled (and other conditions). To validate that @@ -77,31 +73,33 @@ import java.util.Arrays; * Note that this broadcast is not sticky - you should register for it and then check the * above API to avoid a race condition. * <p> - * An application must use {@link #connect(Looper, WifiNanEventCallback)} to initialize a NAN + * An application must use {@link #connect(Handler, WifiNanEventCallback)} to initialize a NAN * cluster - before making any other NAN operation. NAN cluster membership is a device-wide * operation - the API guarantees that the device is in a cluster or joins a NAN cluster (or * starts one if none can be found). Information about connection success (or failure) are * returned in callbacks of {@link WifiNanEventCallback}. Proceed with NAN discovery or * connection setup only after receiving confirmation that NAN connection succeeded - - * {@link WifiNanEventCallback#onConnectSuccess()}. - * When an application is finished using NAN it <b>must</b> use the {@link #disconnect()} API + * {@link WifiNanEventCallback#onConnectSuccess(WifiNanSession)}. When an application is + * finished using NAN it <b>must</b> use the {@link WifiNanSession#disconnect()} API * to indicate to the NAN service that the device may disconnect from the NAN cluster. The * device will actually disconnect from the NAN cluster once the last application disconnects. * <p> * Once a NAN connection is confirmed use the - * {@link #publish(PublishConfig, WifiNanSessionCallback)} or - * {@link #subscribe(SubscribeConfig, WifiNanSessionCallback)} to create publish or subscribe - * NAN discovery sessions. Events are called on the provided callback object - * {@link WifiNanSessionCallback}. Specifically, the - * {@link WifiNanSessionCallback#onPublishStarted(WifiNanPublishSession)} and - * {@link WifiNanSessionCallback#onSubscribeStarted(WifiNanSubscribeSession)} return - * {@link WifiNanPublishSession} and {@link WifiNanSubscribeSession} objects respectively on - * which additional session operations can be performed, e.g. updating the session - * {@link WifiNanPublishSession#updatePublish(PublishConfig)} and - * {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}. Sessions can also be - * used to send messages using the {@link WifiNanSession#sendMessage(int, byte[], int)} APIs. - * When an application is finished with a discovery session it <b>must</b> terminate it using - * the {@link WifiNanSession#terminate()} API. + * {@link WifiNanSession#publish(PublishConfig, WifiNanDiscoverySessionCallback)} or + * {@link WifiNanSession#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)} to + * create publish or subscribe NAN discovery sessions. Events are called on the provided + * callback object {@link WifiNanDiscoverySessionCallback}. Specifically, the + * {@link WifiNanDiscoverySessionCallback#onPublishStarted(WifiNanPublishDiscoverySession)} + * and + * {@link WifiNanDiscoverySessionCallback#onSubscribeStarted(WifiNanSubscribeDiscoverySession)} + * return {@link WifiNanPublishDiscoverySession} and {@link WifiNanSubscribeDiscoverySession} + * objects respectively on which additional session operations can be performed, e.g. updating + * the session {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)} and + * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can also + * be used to send messages using the + * {@link WifiNanDiscoveryBaseSession#sendMessage(int, byte[], int)} APIs. When an application + * is finished with a discovery session it <b>must</b> terminate it using the + * {@link WifiNanDiscoveryBaseSession#terminate()} API. * <p> * Creating connections between NAN devices is managed by the standard * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}. @@ -110,8 +108,8 @@ import java.util.Arrays; * <li>{@link NetworkRequest.Builder#addTransportType(int)} of * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}. * <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using - * {@link #createNetworkSpecifier(int, byte[], byte[])} or - * {@link WifiNanSession#createNetworkSpecifier(int, int, byte[])}. + * {@link WifiNanSession#createNetworkSpecifier(int, byte[], byte[])} or + * {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[])}. * </ul> * * @hide PROPOSED_NAN_API @@ -121,8 +119,6 @@ public class WifiNanManager { private static final boolean DBG = false; private static final boolean VDBG = false; // STOPSHIP if true - private static final int INVALID_CLIENT_ID = 0; - /** * Keys used to generate a Network Specifier for the NAN network request. The network specifier * is formatted as a JSON string. @@ -249,8 +245,8 @@ public class WifiNanManager { * Connection creation role is that of INITIATOR. Used to create a network specifier string * when requesting a NAN network. * - * @see WifiNanSession#createNetworkSpecifier(int, int, byte[]) - * @see #createNetworkSpecifier(int, byte[], byte[]) + * @see WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[]) + * @see WifiNanSession#createNetworkSpecifier(int, byte[], byte[]) */ public static final int WIFI_NAN_DATA_PATH_ROLE_INITIATOR = 0; @@ -258,27 +254,17 @@ public class WifiNanManager { * Connection creation role is that of RESPONDER. Used to create a network specifier string * when requesting a NAN network. * - * @see WifiNanSession#createNetworkSpecifier(int, int, byte[]) - * @see #createNetworkSpecifier(int, byte[], byte[]) + * @see WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[]) + * @see WifiNanSession#createNetworkSpecifier(int, byte[], byte[]) */ public static final int WIFI_NAN_DATA_PATH_ROLE_RESPONDER = 1; private final Context mContext; private final IWifiNanManager mService; - private final CloseGuard mCloseGuard = CloseGuard.get(); private final Object mLock = new Object(); // lock access to the following vars @GuardedBy("mLock") - private final IBinder mBinder = new Binder(); - - @GuardedBy("mLock") - private int mClientId = INVALID_CLIENT_ID; - - @GuardedBy("mLock") - private Looper mLooper; - - @GuardedBy("mLock") private SparseArray<RttManager.RttListener> mRangingListeners = new SparseArray<>(); /** @hide */ @@ -337,19 +323,19 @@ public class WifiNanManager { * create connection to peers. The device will connect to an existing cluster if it can find * one or create a new cluster (if it is the first to enable NAN in its vicinity). Results * (e.g. successful connection to a cluster) are provided to the {@code callback} object. - * An application <b>must</b> call {@link #disconnect()} when done with the Wi-Fi NAN - * connection. + * An application <b>must</b> call {@link WifiNanSession#disconnect()} when done with the + * Wi-Fi NAN connection. * <p> * Note: a NAN cluster is a shared resource - if the device is already connected to a cluster * than this function will simply indicate success immediately. * - * @param looper The Looper on which to execute all callbacks related to the + * @param handler The Handler on whose thread to execute all callbacks related to the * connection - including all sessions opened as part of this - * connection. + * connection. If a null is provided then the application's main thread will be used. * @param callback A callback extended from {@link WifiNanEventCallback}. */ - public void connect(@NonNull Looper looper, @NonNull WifiNanEventCallback callback) { - connect(looper, null, callback); + public void connect(@Nullable Handler handler, @NonNull WifiNanEventCallback callback) { + connect(handler, null, callback); } /** @@ -357,72 +343,46 @@ public class WifiNanManager { * create connection to peers. The device will connect to an existing cluster if it can find * one or create a new cluster (if it is the first to enable NAN in its vicinity). Results * (e.g. successful connection to a cluster) are provided to the {@code callback} object. - * An application <b>must</b> call {@link #disconnect()} when done with the Wi-Fi NAN - * connection. Allows requesting a specific configuration using {@link ConfigRequest}. If not - * necessary (default configuration should usually work) use the - * {@link #connect(Looper, WifiNanEventCallback)} method instead. + * An application <b>must</b> call {@link WifiNanSession#disconnect()} when done with the + * Wi-Fi NAN connection. Allows requesting a specific configuration using + * {@link ConfigRequest}. If not necessary (default configuration should usually work) use + * the {@link #connect(Handler, WifiNanEventCallback)} method instead. * <p> * Note: a NAN cluster is a shared resource - if the device is already connected to a cluster * than this function will simply indicate success immediately. * - * @param looper The Looper on which to execute all callbacks related to the + * @param handler The Handler on whose thread to execute all callbacks related to the * connection - including all sessions opened as part of this - * connection. + * connection. If a null is provided then the application's main thread will be used. * @param configRequest The requested NAN configuration. * @param callback A callback extended from {@link WifiNanEventCallback}. */ - public void connect(@NonNull Looper looper, @Nullable ConfigRequest configRequest, + public void connect(@Nullable Handler handler, @Nullable ConfigRequest configRequest, @NonNull WifiNanEventCallback callback) { if (VDBG) { - Log.v(TAG, "connect(): looper=" + looper + ", callback=" + callback + ", configRequest=" - + configRequest); + Log.v(TAG, + "connect(): handler=" + handler + ", callback=" + callback + ", configRequest=" + + configRequest); } synchronized (mLock) { - mLooper = looper; + Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper(); try { - mClientId = mService.connect(mBinder, mContext.getOpPackageName(), - new WifiNanEventCallbackProxy(this, looper, callback), configRequest); + Binder binder = new Binder(); + mService.connect(binder, mContext.getOpPackageName(), + new WifiNanEventCallbackProxy(this, looper, binder, callback), + configRequest); } catch (RemoteException e) { - mClientId = INVALID_CLIENT_ID; - mLooper = null; throw e.rethrowFromSystemServer(); } } - - mCloseGuard.open("disconnect"); } - /** - * Disconnect from the Wi-Fi NAN service and, if no other applications are connected to NAN, - * also disconnect from the NAN cluster. This method destroys all outstanding operations - - * i.e. all publish and subscribes are terminated, and any outstanding data-links are - * shut-down. However, it is good practice to terminate these discovery sessions and - * connections explicitly before a disconnect. - * <p> - * An application may re-connect after a disconnect using - * {@link WifiNanManager#connect(Looper, WifiNanEventCallback)} . - */ - public void disconnect() { + /** @hide */ + public void disconnect(int clientId, Binder binder) { if (VDBG) Log.v(TAG, "disconnect()"); - IBinder binder; - int clientId; - synchronized (mLock) { - if (mClientId == INVALID_CLIENT_ID) { - Log.w(TAG, "disconnect(): called with invalid client ID - not connected first?"); - return; - } - - binder = mBinder; - clientId = mClientId; - - mLooper = null; - mClientId = INVALID_CLIENT_ID; - } - - mCloseGuard.close(); try { mService.disconnect(clientId, binder); } catch (RemoteException e) { @@ -431,78 +391,26 @@ public class WifiNanManager { } /** @hide */ - @Override - protected void finalize() throws Throwable { - try { - mCloseGuard.warnIfOpen(); - disconnect(); - } finally { - super.finalize(); - } - } - - /** - * Issue a request to the NAN service to create a new NAN publish discovery session, using - * the specified {@code publishConfig} configuration. The results of the publish operation - * are routed to the callbacks of {@link WifiNanSessionCallback}: - * <ul> - * <li>{@link WifiNanSessionCallback#onPublishStarted(WifiNanPublishSession)} is called - * when the publish session is created and provides a handle to the session. Further - * operations on the publish session can be executed on that object. - * <li>{@link WifiNanSessionCallback#onSessionConfigFail(int)} is called if the publish - * operation failed. - * </ul> - * <p> - * Other results of the publish session operations will also be routed to callbacks - * on the {@code callback} object. The resulting publish session can be modified using - * {@link WifiNanPublishSession#updatePublish(PublishConfig)}. - * <p> - * An application must use the {@link WifiNanSession#terminate()} to terminate the publish - * discovery session once it isn't needed. This will free resources as well terminate - * any on-air transmissions. - * - * @param publishConfig The {@link PublishConfig} specifying the - * configuration of the requested publish session. - * @param callback A {@link WifiNanSessionCallback} derived object to be used for session - * event callbacks. - */ - public void publish(@NonNull PublishConfig publishConfig, - @NonNull WifiNanSessionCallback callback) { - if (VDBG) Log.v(TAG, "publish(): config=" + publishConfig); + public void publish(int clientId, Looper looper, PublishConfig publishConfig, + WifiNanDiscoverySessionCallback callback) { + if (VDBG) Log.v(TAG, "publish(): clientId=" + clientId + ", config=" + publishConfig); - int clientId; - Looper looper; - synchronized (mLock) { - if (mLooper == null || mClientId == INVALID_CLIENT_ID) { - Log.e(TAG, "publish(): called with null looper or invalid client ID - " - + "not connected first?"); - return; - } - - clientId = mClientId; - looper = mLooper; - } try { mService.publish(clientId, publishConfig, - new WifiNanSessionCallbackProxy(this, looper, true, callback)); + new WifiNanDiscoverySessionCallbackProxy(this, looper, true, callback, + clientId)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** @hide */ - public void updatePublish(int sessionId, PublishConfig publishConfig) { - if (VDBG) Log.v(TAG, "updatePublish(): config=" + publishConfig); - - int clientId; - synchronized (mLock) { - if (mClientId == INVALID_CLIENT_ID) { - Log.e(TAG, "updatePublish(): called with invalid client ID - not connected first?"); - return; - } - - clientId = mClientId; + public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) { + if (VDBG) { + Log.v(TAG, "updatePublish(): clientId=" + clientId + ",sessionId=" + sessionId + + ", config=" + publishConfig); } + try { mService.updatePublish(clientId, sessionId, publishConfig); } catch (RemoteException e) { @@ -510,73 +418,30 @@ public class WifiNanManager { } } - /** - * Issue a request to the NAN service to create a new NAN subscribe discovery session, using - * the specified {@code subscribeConfig} configuration. The results of the subscribe - * operation are routed to the callbacks of {@link WifiNanSessionCallback}: - * <ul> - * <li>{@link WifiNanSessionCallback#onSubscribeStarted(WifiNanSubscribeSession)} is called - * when the subscribe session is created and provides a handle to the session. Further - * operations on the subscribe session can be executed on that object. - * <li>{@link WifiNanSessionCallback#onSessionConfigFail(int)} is called if the subscribe - * operation failed. - * </ul> - * <p> - * Other results of the subscribe session operations will also be routed to callbacks - * on the {@code callback} object. The resulting subscribe session can be modified using - * {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}. - * <p> - * An application must use the {@link WifiNanSession#terminate()} to terminate the - * subscribe discovery session once it isn't needed. This will free resources as well - * terminate any on-air transmissions. - * - * @param subscribeConfig The {@link SubscribeConfig} specifying the - * configuration of the requested subscribe session. - * @param callback A {@link WifiNanSessionCallback} derived object to be used for session - * event callbacks. - */ - public void subscribe(@NonNull SubscribeConfig subscribeConfig, - @NonNull WifiNanSessionCallback callback) { + /** @hide */ + public void subscribe(int clientId, Looper looper, SubscribeConfig subscribeConfig, + WifiNanDiscoverySessionCallback callback) { if (VDBG) { - Log.v(TAG, "subscribe(): config=" + subscribeConfig); - } - - int clientId; - Looper looper; - synchronized (mLock) { - if (mLooper == null || mClientId == INVALID_CLIENT_ID) { - Log.e(TAG, "subscribe(): called with null looper or invalid client ID - " - + "not connected first?"); - return; + if (VDBG) { + Log.v(TAG, + "subscribe(): clientId=" + clientId + ", config=" + subscribeConfig); } - - clientId = mClientId; - looper = mLooper; } try { mService.subscribe(clientId, subscribeConfig, - new WifiNanSessionCallbackProxy(this, looper, false, callback)); + new WifiNanDiscoverySessionCallbackProxy(this, looper, false, callback, + clientId)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** @hide */ - public void updateSubscribe(int sessionId, SubscribeConfig subscribeConfig) { + public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) { if (VDBG) { - Log.v(TAG, "subscribe(): config=" + subscribeConfig); - } - - int clientId; - synchronized (mLock) { - if (mClientId == INVALID_CLIENT_ID) { - Log.e(TAG, - "updateSubscribe(): called with invalid client ID - not connected first?"); - return; - } - - clientId = mClientId; + Log.v(TAG, "updateSubscribe(): clientId=" + clientId + ",sessionId=" + sessionId + + ", config=" + subscribeConfig); } try { @@ -587,18 +452,10 @@ public class WifiNanManager { } /** @hide */ - public void terminateSession(int sessionId) { - if (DBG) Log.d(TAG, "Terminate NAN session #" + sessionId); - - int clientId; - synchronized (mLock) { - if (mClientId == INVALID_CLIENT_ID) { - Log.e(TAG, - "terminateSession(): called with invalid client ID - not connected first?"); - return; - } - - clientId = mClientId; + public void terminateSession(int clientId, int sessionId) { + if (VDBG) { + Log.d(TAG, + "terminateSession(): clientId=" + clientId + ", sessionId=" + sessionId); } try { @@ -609,21 +466,12 @@ public class WifiNanManager { } /** @hide */ - public void sendMessage(int sessionId, int peerId, byte[] message, int messageId, + public void sendMessage(int clientId, int sessionId, int peerId, byte[] message, int messageId, int retryCount) { if (VDBG) { - Log.v(TAG, "sendMessage(): sessionId=" + sessionId + ", peerId=" + peerId - + ", messageId=" + messageId + ", retryCount=" + retryCount); - } - - int clientId; - synchronized (mLock) { - if (mClientId == INVALID_CLIENT_ID) { - Log.e(TAG, "sendMessage(): called with invalid client ID - not connected first?"); - return; - } - - clientId = mClientId; + Log.v(TAG, + "sendMessage(): clientId=" + clientId + ", sessionId=" + sessionId + ", peerId=" + + peerId + ", messageId=" + messageId + ", retryCount=" + retryCount); } try { @@ -634,21 +482,11 @@ public class WifiNanManager { } /** @hide */ - public void startRanging(int sessionId, RttManager.RttParams[] params, + public void startRanging(int clientId, int sessionId, RttManager.RttParams[] params, RttManager.RttListener listener) { if (VDBG) { - Log.v(TAG, "startRanging: sessionId=" + sessionId + ", " + "params=" - + Arrays.toString(params) + ", listener=" + listener); - } - - int clientId; - synchronized (mLock) { - if (mClientId == INVALID_CLIENT_ID) { - Log.e(TAG, "startRanging(): called with invalid client ID - not connected first?"); - return; - } - - clientId = mClientId; + Log.v(TAG, "startRanging: clientId=" + clientId + ", sessionId=" + sessionId + ", " + + "params=" + Arrays.toString(params) + ", listener=" + listener); } int rangingKey = 0; @@ -665,7 +503,7 @@ public class WifiNanManager { } /** @hide */ - public String createNetworkSpecifier(@DataPathRole int role, int sessionId, int peerId, + public String createNetworkSpecifier(int clientId, int role, int sessionId, int peerId, byte[] token) { if (VDBG) { Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId @@ -701,18 +539,6 @@ public class WifiNanManager { } } - int clientId; - synchronized (mLock) { - if (mClientId == INVALID_CLIENT_ID) { - Log.e(TAG, - "createNetworkSpecifier: called with invalid client ID - not connected " - + "first?"); - return null; - } - - clientId = mClientId; - } - JSONObject json; try { json = new JSONObject(); @@ -734,36 +560,8 @@ public class WifiNanManager { return json.toString(); } - /** - * Create a {@link NetworkRequest.Builder#setNetworkSpecifier(String)} for a - * WiFi NAN connection to the specified peer. The - * {@link NetworkRequest.Builder#addTransportType(int)} should be set to - * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}. - * <p> - * This API is targeted for applications which can obtain the peer MAC address using OOB - * (out-of-band) discovery. NAN discovery does not provide the MAC address of the peer - - * when using NAN discovery use the alternative network specifier method - - * {@link WifiNanSession#createNetworkSpecifier(int, int, byte[])}. - * - * @param role The role of this device: - * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_INITIATOR} or - * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_RESPONDER} - * @param peer The MAC address of the peer's NAN discovery interface. On a RESPONDER this - * value is used to gate the acceptance of a connection request from only that - * peer. A RESPONDER may specified a null - indicating that it will accept - * connection requests from any device. - * @param token An arbitrary token (message) to be used to match connection initiation request - * to a responder setup. A RESPONDER is set up with a {@code token} which must - * be matched by the token provided by the INITIATOR. A null token is permitted - * on the RESPONDER and matches any peer token. An empty ({@code ""}) token is - * not the same as a null token and requires the peer token to be empty as well. - * - * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to {@link - * android.net.ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)} - * [or other varieties of that API]. - */ - public String createNetworkSpecifier(@DataPathRole int role, @Nullable byte[] peer, + /** @hide */ + public String createNetworkSpecifier(int clientId, @DataPathRole int role, @Nullable byte[] peer, @Nullable byte[] token) { if (VDBG) { Log.v(TAG, "createNetworkSpecifier: role=" + role + ", token=" + token); @@ -802,18 +600,6 @@ public class WifiNanManager { } } - int clientId; - synchronized (mLock) { - if (mClientId == INVALID_CLIENT_ID) { - Log.e(TAG, - "createNetworkSpecifier: called with invalid client ID - not connected " - + "first?"); - return null; - } - - clientId = mClientId; - } - JSONObject json; try { json = new JSONObject(); @@ -844,6 +630,8 @@ public class WifiNanManager { private final Handler mHandler; private final WeakReference<WifiNanManager> mNanManager; + private final Binder mBinder; + private final Looper mLooper; RttManager.RttListener getAndRemoveRangingListener(int rangingId) { WifiNanManager mgr = mNanManager.get(); @@ -865,9 +653,11 @@ public class WifiNanManager { * * @param looper The looper on which to execute the callbacks. */ - WifiNanEventCallbackProxy(WifiNanManager mgr, Looper looper, + WifiNanEventCallbackProxy(WifiNanManager mgr, Looper looper, Binder binder, final WifiNanEventCallback originalCallback) { mNanManager = new WeakReference<>(mgr); + mLooper = looper; + mBinder = binder; if (VDBG) Log.v(TAG, "WifiNanEventCallbackProxy ctor: looper=" + looper); mHandler = new Handler(looper) { @@ -885,13 +675,10 @@ public class WifiNanManager { switch (msg.what) { case CALLBACK_CONNECT_SUCCESS: - originalCallback.onConnectSuccess(); + originalCallback.onConnectSuccess( + new WifiNanSession(mgr, mBinder, mLooper, msg.arg1)); break; case CALLBACK_CONNECT_FAIL: - synchronized (mgr.mLock) { - mgr.mLooper = null; - mgr.mClientId = INVALID_CLIENT_ID; - } mNanManager.clear(); originalCallback.onConnectFail(msg.arg1); break; @@ -935,10 +722,11 @@ public class WifiNanManager { } @Override - public void onConnectSuccess() { + public void onConnectSuccess(int clientId) { if (VDBG) Log.v(TAG, "onConnectSuccess"); Message msg = mHandler.obtainMessage(CALLBACK_CONNECT_SUCCESS); + msg.arg1 = clientId; mHandler.sendMessage(msg); } @@ -998,7 +786,8 @@ public class WifiNanManager { } } - private static class WifiNanSessionCallbackProxy extends IWifiNanSessionCallback.Stub { + private static class WifiNanDiscoverySessionCallbackProxy extends + IWifiNanDiscoverySessionCallback.Stub { private static final int CALLBACK_SESSION_STARTED = 0; private static final int CALLBACK_SESSION_CONFIG_SUCCESS = 1; private static final int CALLBACK_SESSION_CONFIG_FAIL = 2; @@ -1013,18 +802,22 @@ public class WifiNanManager { private final WeakReference<WifiNanManager> mNanManager; private final boolean mIsPublish; - private final WifiNanSessionCallback mOriginalCallback; + private final WifiNanDiscoverySessionCallback mOriginalCallback; + private final int mClientId; private final Handler mHandler; - private WifiNanSession mSession; + private WifiNanDiscoveryBaseSession mSession; - WifiNanSessionCallbackProxy(WifiNanManager mgr, Looper looper, boolean isPublish, - WifiNanSessionCallback originalCallback) { + WifiNanDiscoverySessionCallbackProxy(WifiNanManager mgr, Looper looper, boolean isPublish, + WifiNanDiscoverySessionCallback originalCallback, int clientId) { mNanManager = new WeakReference<>(mgr); mIsPublish = isPublish; mOriginalCallback = originalCallback; + mClientId = clientId; - if (VDBG) Log.v(TAG, "WifiNanSessionCallbackProxy ctor: isPublish=" + isPublish); + if (VDBG) { + Log.v(TAG, "WifiNanDiscoverySessionCallbackProxy ctor: isPublish=" + isPublish); + } mHandler = new Handler(looper) { @Override @@ -1032,7 +825,7 @@ public class WifiNanManager { if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg); if (mNanManager.get() == null) { - Log.w(TAG, "WifiNanSessionCallbackProxy: handleMessage post GC"); + Log.w(TAG, "WifiNanDiscoverySessionCallbackProxy: handleMessage post GC"); return; } @@ -1175,11 +968,13 @@ public class WifiNanManager { } if (mIsPublish) { - WifiNanPublishSession session = new WifiNanPublishSession(mgr, sessionId); + WifiNanPublishDiscoverySession session = new WifiNanPublishDiscoverySession(mgr, + mClientId, sessionId); mSession = session; mOriginalCallback.onPublishStarted(session); } else { - WifiNanSubscribeSession session = new WifiNanSubscribeSession(mgr, sessionId); + WifiNanSubscribeDiscoverySession + session = new WifiNanSubscribeDiscoverySession(mgr, mClientId, sessionId); mSession = session; mOriginalCallback.onSubscribeStarted(session); } diff --git a/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java b/wifi/java/android/net/wifi/nan/WifiNanPublishDiscoverySession.java index ccd7faee8476..c1731bbfd817 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java +++ b/wifi/java/android/net/wifi/nan/WifiNanPublishDiscoverySession.java @@ -21,32 +21,33 @@ import android.util.Log; /** * A class representing a NAN publish session. Created when - * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} is called and a + * {@link WifiNanManager#publish(PublishConfig, WifiNanDiscoverySessionCallback)} is called and a * discovery session is created and returned in - * {@link WifiNanSessionCallback#onPublishStarted(WifiNanPublishSession)}. See baseline - * functionality of all discovery sessions in {@link WifiNanSession}. This object allows updating - * an existing/running publish discovery session using {@link #updatePublish(PublishConfig)}. + * {@link WifiNanDiscoverySessionCallback#onPublishStarted(WifiNanPublishDiscoverySession)}. See + * baseline functionality of all discovery sessions in {@link WifiNanDiscoveryBaseSession}. This + * object allows updating an existing/running publish discovery session using + * {@link #updatePublish(PublishConfig)}. * * @hide PROPOSED_NAN_API */ -public class WifiNanPublishSession extends WifiNanSession { - private static final String TAG = "WifiNanPublishSession"; +public class WifiNanPublishDiscoverySession extends WifiNanDiscoveryBaseSession { + private static final String TAG = "WifiNanPublishDiscSsn"; /** @hide */ - public WifiNanPublishSession(WifiNanManager manager, int sessionId) { - super(manager, sessionId); + public WifiNanPublishDiscoverySession(WifiNanManager manager, int clientId, int sessionId) { + super(manager, clientId, sessionId); } /** * Re-configure the currently active publish session. The - * {@link WifiNanSessionCallback} is not replaced - the same listener used + * {@link WifiNanDiscoverySessionCallback} is not replaced - the same listener used * at creation is still used. The results of the configuration are returned using - * {@link WifiNanSessionCallback}: + * {@link WifiNanDiscoverySessionCallback}: * <ul> - * <li>{@link WifiNanSessionCallback#onSessionConfigSuccess()}: configuration update - * succeeded. - * <li>{@link WifiNanSessionCallback#onSessionConfigFail(int)}: configuration update - * failed. The publish discovery session is still running using its previous + * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigSuccess()}: configuration + * update succeeded. + * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFail(int)}: configuration + * update failed. The publish discovery session is still running using its previous * configuration (i.e. update failure does not terminate the session). * </ul> * @@ -63,7 +64,7 @@ public class WifiNanPublishSession extends WifiNanSession { return; } - mgr.updatePublish(mSessionId, publishConfig); + mgr.updatePublish(mClientId, mSessionId, publishConfig); } } } diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanSession.java index 005ca291cbf5..f9e601795552 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanSession.java +++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java @@ -16,9 +16,12 @@ package android.net.wifi.nan; +import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.net.wifi.RttManager; +import android.net.NetworkRequest; +import android.os.Binder; +import android.os.Handler; +import android.os.Looper; import android.util.Log; import dalvik.system.CloseGuard; @@ -26,18 +29,8 @@ import dalvik.system.CloseGuard; import java.lang.ref.WeakReference; /** - * A class representing a single publish or subscribe NAN session. This object - * will not be created directly - only its child classes are available: - * {@link WifiNanPublishSession} and {@link WifiNanSubscribeSession}. This class provides - * functionality common to both publish and subscribe discovery sessions: - * <ul> - * <li>Sending messages: {@link #sendMessage(int, byte[], int)} or - * {@link #sendMessage(int, byte[], int, int)} methods. - * <li>Creating a network-specifier when requesting a NAN connection: - * {@link #createNetworkSpecifier(int, int, byte[])}. - * </ul> - * The {@link #terminate()} method must be called to terminate discovery sessions once they are - * no longer needed. + * This class represents a Wi-Fi NAN session - an attachment to the Wi-Fi NAN service through + * which the app can execute discovery operations. * * @hide PROPOSED_NAN_API */ @@ -46,71 +39,44 @@ public class WifiNanSession { private static final boolean DBG = false; private static final boolean VDBG = false; // STOPSHIP if true - private static final int MAX_SEND_RETRY_COUNT = 5; - - /** @hide */ - protected WeakReference<WifiNanManager> mMgr; - /** @hide */ - protected final int mSessionId; - /** @hide */ - protected boolean mTerminated = false; + private final WeakReference<WifiNanManager> mMgr; + private final Binder mBinder; + private final Looper mLooper; + private final int mClientId; + private boolean mTerminated = true; private final CloseGuard mCloseGuard = CloseGuard.get(); - /** - * Return the maximum permitted retry count when sending messages using - * {@link #sendMessage(int, byte[], int, int)}. - * - * @return Maximum retry count when sending messages. - */ - public static int getMaxSendRetryCount() { - return MAX_SEND_RETRY_COUNT; - } - /** @hide */ - public WifiNanSession(WifiNanManager manager, int sessionId) { - if (VDBG) Log.v(TAG, "New client created: manager=" + manager + ", sessionId=" + sessionId); + public WifiNanSession(WifiNanManager manager, Binder binder, Looper looper, int clientId) { + if (VDBG) Log.v(TAG, "New session created: manager=" + manager + ", clientId=" + clientId); mMgr = new WeakReference<>(manager); - mSessionId = sessionId; + mBinder = binder; + mLooper = looper; + mClientId = clientId; + mTerminated = false; - mCloseGuard.open("terminate"); + mCloseGuard.open("disconnect"); } /** - * Terminate the publish or subscribe session - free any resources, and stop - * transmitting packets on-air (for an active session) or listening for - * matches (for a passive session). The session may not be used for any - * additional operations after termination. + * Disconnect from the Wi-Fi NAN service and, if no other applications are connected to NAN, + * also disconnect from the NAN cluster. This method destroys all outstanding operations - + * i.e. all publish and subscribes are terminated, and any outstanding data-links are + * shut-down. However, it is good practice to terminate these discovery sessions and + * connections explicitly before a disconnect. * <p> - * This operation must be done on a session which is no longer needed. Otherwise system - * resources will continue to be utilized until the application terminates. The only - * exception is a session for which we received a termination callback, - * {@link WifiNanSessionCallback#onSessionTerminated(int)}. + * An application may re-connect after a disconnect using + * {@link WifiNanManager#connect(Handler, WifiNanEventCallback)} . */ - public void terminate() { + public void disconnect() { WifiNanManager mgr = mMgr.get(); if (mgr == null) { - Log.w(TAG, "terminate: called post GC on WifiNanManager"); - return; - } - mgr.terminateSession(mSessionId); - mTerminated = true; - mMgr.clear(); - mCloseGuard.close(); - } - - /** - * Sets the status of the session to terminated - i.e. an indication that - * already terminated rather than executing a termination. - * - * @hide - */ - public void setTerminated() { - if (mTerminated) { - Log.w(TAG, "terminate: already terminated."); + Log.w(TAG, "disconnect: called post GC on WifiNanManager"); return; } + mgr.disconnect(mClientId, mBinder); mTerminated = true; mMgr.clear(); mCloseGuard.close(); @@ -122,7 +88,7 @@ public class WifiNanSession { try { if (!mTerminated) { mCloseGuard.warnIfOpen(); - terminate(); + disconnect(); } } finally { super.finalize(); @@ -130,125 +96,100 @@ public class WifiNanSession { } /** - * Sends a message to the specified destination. NAN messages are transmitted in the context - * of a discovery session - executed subsequent to a publish/subscribe - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} event. + * Issue a request to the NAN service to create a new NAN publish discovery session, using + * the specified {@code publishConfig} configuration. The results of the publish operation + * are routed to the callbacks of {@link WifiNanDiscoverySessionCallback}: + * <ul> + * <li>{@link WifiNanDiscoverySessionCallback#onPublishStarted(WifiNanPublishDiscoverySession)} + * is called when the publish session is created and provides a handle to the session. + * Further operations on the publish session can be executed on that object. + * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFail(int)} is called if the + * publish operation failed. + * </ul> * <p> - * NAN messages are not guaranteed delivery. Callbacks on {@link WifiNanSessionCallback} - * indicate message was transmitted successfully, - * {@link WifiNanSessionCallback#onMessageSendSuccess(int)}, or transmission failed - * (possibly after several retries) - - * {@link WifiNanSessionCallback#onMessageSendFail(int, int)}. + * Other results of the publish session operations will also be routed to callbacks + * on the {@code callback} object. The resulting publish session can be modified using + * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)}. * <p> - * The peer will get a callback indicating a message was received using - * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])}. + * An application must use the {@link WifiNanDiscoveryBaseSession#terminate()} to + * terminate the publish discovery session once it isn't needed. This will free + * resources as well terminate any on-air transmissions. * - * @param peerId The peer's ID for the message. Must be a result of an - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or - * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])} events. - * @param message The message to be transmitted. - * @param messageId An arbitrary integer used by the caller to identify the message. The same - * integer ID will be returned in the callbacks indicating message send success or - * failure. The {@code messageId} is not used internally by the NAN service - it - * can be arbitrary and non-unique. - * @param retryCount An integer specifying how many additional service-level (as opposed to PHY - * or MAC level) retries should be attempted if there is no ACK from the receiver - * (note: no retransmissions are attempted in other failure cases). A value of 0 - * indicates no retries. Max permitted value is {@link #getMaxSendRetryCount()}. + * @param publishConfig The {@link PublishConfig} specifying the + * configuration of the requested publish session. + * @param callback A {@link WifiNanDiscoverySessionCallback} derived object to be used for + * session event callbacks. */ - public void sendMessage(int peerId, @Nullable byte[] message, int messageId, int retryCount) { + public void publish(@NonNull PublishConfig publishConfig, + @NonNull WifiNanDiscoverySessionCallback callback) { + WifiNanManager mgr = mMgr.get(); + if (mgr == null) { + Log.e(TAG, "publish: called post GC on WifiNanManager"); + return; + } if (mTerminated) { - Log.w(TAG, "sendMessage: called on terminated session"); + Log.e(TAG, "publish: called after termination"); return; - } else { - WifiNanManager mgr = mMgr.get(); - if (mgr == null) { - Log.w(TAG, "sendMessage: called post GC on WifiNanManager"); - return; - } - - mgr.sendMessage(mSessionId, peerId, message, messageId, retryCount); } + mgr.publish(mClientId, mLooper, publishConfig, callback); } /** - * Sends a message to the specified destination. NAN messages are transmitted in the context - * of a discovery session - executed subsequent to a publish/subscribe - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} event. + * Issue a request to the NAN service to create a new NAN subscribe discovery session, using + * the specified {@code subscribeConfig} configuration. The results of the subscribe + * operation are routed to the callbacks of {@link WifiNanDiscoverySessionCallback}: + * <ul> + * <li>{@link WifiNanDiscoverySessionCallback#onSubscribeStarted(WifiNanSubscribeDiscoverySession)} + * is called when the subscribe session is created and provides a handle to the session. + * Further operations on the subscribe session can be executed on that object. + * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFail(int)} is called if the + * subscribe operation failed. + * </ul> * <p> - * NAN messages are not guaranteed delivery. Callbacks on {@link WifiNanSessionCallback} - * indicate message was transmitted successfully, - * {@link WifiNanSessionCallback#onMessageSendSuccess(int)}, or transmission failed - * (possibly after several retries) - - * {@link WifiNanSessionCallback#onMessageSendFail(int, int)}. + * Other results of the subscribe session operations will also be routed to callbacks + * on the {@code callback} object. The resulting subscribe session can be modified using + * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. * <p> - * The peer will get a callback indicating a message was received using - * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])}. - * Equivalent to {@link #sendMessage(int, byte[], int, int)} with a {@code retryCount} of - * 0. + * An application must use the {@link WifiNanDiscoveryBaseSession#terminate()} to + * terminate the subscribe discovery session once it isn't needed. This will free + * resources as well terminate any on-air transmissions. * - * @param peerId The peer's ID for the message. Must be a result of an - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or - * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])} events. - * @param message The message to be transmitted. - * @param messageId An arbitrary integer used by the caller to identify the message. The same - * integer ID will be returned in the callbacks indicating message send success or - * failure. The {@code messageId} is not used internally by the NAN service - it - * can be arbitrary and non-unique. + * @param subscribeConfig The {@link SubscribeConfig} specifying the + * configuration of the requested subscribe session. + * @param callback A {@link WifiNanDiscoverySessionCallback} derived object to be used for + * session event callbacks. */ - public void sendMessage(int peerId, @Nullable byte[] message, int messageId) { - sendMessage(peerId, message, messageId, 0); - } - - /** - * Start a ranging operation with the specified peers. The peer IDs are obtained from an - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or - * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])} operation - can only - * range devices which are part of an ongoing discovery session. - * - * @param params RTT parameters - each corresponding to a specific peer ID (the array sizes - * must be identical). The - * {@link android.net.wifi.RttManager.RttParams#bssid} member must be set to - * a peer ID - not to a MAC address. - * @param listener The listener to receive the results of the ranging session. - * @hide PROPOSED_NAN_SYSTEM_API [TODO: b/28847998 - track RTT API & visilibity] - */ - public void startRanging(RttManager.RttParams[] params, RttManager.RttListener listener) { + public void subscribe(@NonNull SubscribeConfig subscribeConfig, + @NonNull WifiNanDiscoverySessionCallback callback) { + WifiNanManager mgr = mMgr.get(); + if (mgr == null) { + Log.e(TAG, "publish: called post GC on WifiNanManager"); + return; + } if (mTerminated) { - Log.w(TAG, "startRanging: called on terminated session"); + Log.e(TAG, "publish: called after termination"); return; - } else { - WifiNanManager mgr = mMgr.get(); - if (mgr == null) { - Log.w(TAG, "startRanging: called post GC on WifiNanManager"); - return; - } - - mgr.startRanging(mSessionId, params, listener); } + mgr.subscribe(mClientId, mLooper, subscribeConfig, callback); } /** - * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a + * Create a {@link NetworkRequest.Builder#setNetworkSpecifier(String)} for a * WiFi NAN connection to the specified peer. The - * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to + * {@link NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}. * <p> - * This method should be used when setting up a connection with a peer discovered through NAN - * discovery or communication (in such scenarios the MAC address of the peer is shielded by - * an opaque peer ID handle). If a NAN connection is needed to a peer discovered using other - * OOB (out-of-band) mechanism then use the alternative - * {@link WifiNanManager#createNetworkSpecifier(int, byte[], byte[])} method - which uses the - * peer's MAC address. + * This API is targeted for applications which can obtain the peer MAC address using OOB + * (out-of-band) discovery. NAN discovery does not provide the MAC address of the peer - + * when using NAN discovery use the alternative network specifier method - + * {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[])}. * - * @param role The role of this device: - * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_INITIATOR} or - * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_RESPONDER} - * @param peerId The peer ID obtained through - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or - * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])}. On a RESPONDER this + * @param role The role of this device: + * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_INITIATOR} or + * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_RESPONDER} + * @param peer The MAC address of the peer's NAN discovery interface. On a RESPONDER this * value is used to gate the acceptance of a connection request from only that - * peer. A RESPONDER may specified a 0 - indicating that it will accept + * peer. A RESPONDER may specified a null - indicating that it will accept * connection requests from any device. * @param token An arbitrary token (message) to be used to match connection initiation request * to a responder setup. A RESPONDER is set up with a {@code token} which must @@ -258,22 +199,20 @@ public class WifiNanSession { * * @return A string to be used to construct * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to - * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,android.net.ConnectivityManager.NetworkCallback)} + * {@link android.net.ConnectivityManager#requestNetwork(NetworkRequest, android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */ - public String createNetworkSpecifier(@WifiNanManager.DataPathRole int role, int peerId, + public String createNetworkSpecifier(@WifiNanManager.DataPathRole int role, @Nullable byte[] peer, @Nullable byte[] token) { + WifiNanManager mgr = mMgr.get(); + if (mgr == null) { + Log.e(TAG, "createNetworkSpecifier: called post GC on WifiNanManager"); + return ""; + } if (mTerminated) { - Log.w(TAG, "createNetworkSpecifier: called on terminated session"); - return null; - } else { - WifiNanManager mgr = mMgr.get(); - if (mgr == null) { - Log.w(TAG, "createNetworkSpecifier: called post GC on WifiNanManager"); - return null; - } - - return mgr.createNetworkSpecifier(role, mSessionId, peerId, token); + Log.e(TAG, "createNetworkSpecifier: called after termination"); + return ""; } + return mgr.createNetworkSpecifier(mClientId, role, peer, token); } } diff --git a/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java b/wifi/java/android/net/wifi/nan/WifiNanSubscribeDiscoverySession.java index d0e56c521525..b8d77aa1f8e4 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java +++ b/wifi/java/android/net/wifi/nan/WifiNanSubscribeDiscoverySession.java @@ -21,34 +21,35 @@ import android.util.Log; /** * A class representing a NAN subscribe session. Created when - * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} is called and a - * discovery session is created and returned in - * {@link WifiNanSessionCallback#onSubscribeStarted(WifiNanSubscribeSession)}. See baseline - * functionality of all discovery sessions in {@link WifiNanSession}. This object allows updating - * an existing/running subscribe discovery session using {@link #updateSubscribe(SubscribeConfig)}. + * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)} is called + * and a discovery session is created and returned in + * {@link WifiNanDiscoverySessionCallback#onSubscribeStarted(WifiNanSubscribeDiscoverySession)}. + * See baseline functionality of all discovery sessions in {@link WifiNanDiscoveryBaseSession}. + * This object allows updating an existing/running subscribe discovery session using + * {@link #updateSubscribe(SubscribeConfig)}. * * @hide PROPOSED_NAN_API */ -public class WifiNanSubscribeSession extends WifiNanSession { - private static final String TAG = "WifiNanSubscribeSession"; +public class WifiNanSubscribeDiscoverySession extends WifiNanDiscoveryBaseSession { + private static final String TAG = "WifiNanSubscribeDiscSsn"; /** * {@hide} */ - public WifiNanSubscribeSession(WifiNanManager manager, int sessionId) { - super(manager, sessionId); + public WifiNanSubscribeDiscoverySession(WifiNanManager manager, int clientId, int sessionId) { + super(manager, clientId, sessionId); } /** * Re-configure the currently active subscribe session. The - * {@link WifiNanSessionCallback} is not replaced - the same listener used + * {@link WifiNanDiscoverySessionCallback} is not replaced - the same listener used * at creation is still used. The results of the configuration are returned using - * {@link WifiNanSessionCallback}: + * {@link WifiNanDiscoverySessionCallback}: * <ul> - * <li>{@link WifiNanSessionCallback#onSessionConfigSuccess()}: configuration update - * succeeded. - * <li>{@link WifiNanSessionCallback#onSessionConfigFail(int)}: configuration update - * failed. The subscribe discovery session is still running using its previous + * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigSuccess()}: configuration + * update succeeded. + * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFail(int)}: configuration + * update failed. The subscribe discovery session is still running using its previous * configuration (i.e. update failure does not terminate the session). * </ul> * @@ -66,7 +67,7 @@ public class WifiNanSubscribeSession extends WifiNanSession { return; } - mgr.updateSubscribe(mSessionId, subscribeConfig); + mgr.updateSubscribe(mClientId, mSessionId, subscribeConfig); } } } |