summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk18
-rw-r--r--api/current.txt91
-rw-r--r--api/system-current.txt91
-rw-r--r--api/test-current.txt91
-rw-r--r--cmds/app_process/app_main.cpp8
-rw-r--r--core/java/android/bluetooth/BluetoothGattCallbackWrapper.java5
-rw-r--r--core/java/android/bluetooth/IBluetoothGatt.aidl9
-rw-r--r--core/java/android/bluetooth/IBluetoothGattCallback.aidl2
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeAdvertiser.java52
-rw-r--r--core/java/android/bluetooth/le/IAdvertiserCallback.aidl29
-rw-r--r--core/java/android/content/pm/IOtaDexopt.aidl13
-rw-r--r--core/java/android/content/pm/RegisteredServicesCache.java18
-rw-r--r--core/java/android/net/InterfaceConfiguration.java8
-rw-r--r--core/java/android/net/LocalSocketImpl.java10
-rw-r--r--core/java/android/net/NetworkCapabilities.java10
-rw-r--r--core/java/android/os/Environment.java5
-rw-r--r--core/java/android/os/HwBinder.java59
-rw-r--r--core/java/android/os/HwBlob.java74
-rw-r--r--core/java/android/os/HwParcel.java118
-rw-r--r--core/java/android/os/HwRemoteBinder.java56
-rw-r--r--core/java/android/os/IHwBinder.java29
-rw-r--r--core/java/android/os/IHwInterface.java22
-rw-r--r--core/java/android/os/Process.java361
-rw-r--r--core/java/android/os/RecoverySystem.java6
-rw-r--r--core/java/android/os/ZygoteProcess.java447
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java10
-rw-r--r--core/java/com/android/internal/os/PowerProfile.java2
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java10
-rw-r--r--core/java/com/android/internal/os/WebViewZygoteInit.java32
-rw-r--r--core/java/com/android/internal/os/WifiPowerEstimator.java4
-rw-r--r--core/java/com/android/internal/os/WrapperInit.java4
-rw-r--r--core/java/com/android/internal/os/Zygote.java38
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java17
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java204
-rw-r--r--core/java/com/android/internal/os/ZygoteServer.java167
-rw-r--r--core/java/com/android/internal/util/WakeupMessage.java16
-rw-r--r--core/jni/Android.mk9
-rw-r--r--core/jni/AndroidRuntime.cpp8
-rw-r--r--core/jni/android_os_HwBinder.cpp293
-rw-r--r--core/jni/android_os_HwBinder.h60
-rw-r--r--core/jni/android_os_HwBlob.cpp452
-rw-r--r--core/jni/android_os_HwBlob.h91
-rw-r--r--core/jni/android_os_HwParcel.cpp930
-rw-r--r--core/jni/android_os_HwParcel.h75
-rw-r--r--core/jni/android_os_HwRemoteBinder.cpp196
-rw-r--r--core/jni/android_os_HwRemoteBinder.h60
-rw-r--r--core/jni/android_os_SELinux.cpp6
-rw-r--r--core/jni/com_android_internal_content_NativeLibraryHelper.cpp6
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp35
-rw-r--r--core/jni/hwbinder/EphemeralStorage.cpp156
-rw-r--r--core/jni/hwbinder/EphemeralStorage.h81
-rw-r--r--core/res/AndroidManifest.xml1
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk38
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml31
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/res/layout/activity_main.xml7
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/CorruptedDexTest.java68
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/MainActivity.java30
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java4
-rwxr-xr-xdocs/html/guide/topics/renderscript/compute.jd137
-rw-r--r--drm/jni/Android.mk1
-rw-r--r--libs/hwui/ClipArea.h10
-rw-r--r--libs/hwui/DeferredLayerUpdater.h2
-rw-r--r--libs/hwui/FontRenderer.h2
-rw-r--r--libs/hwui/GpuMemoryTracker.h2
-rw-r--r--libs/hwui/PropertyValuesAnimatorSet.h2
-rw-r--r--libs/hwui/RecordedOp.h2
-rw-r--r--libs/hwui/VectorDrawable.h12
-rw-r--r--libs/hwui/hwui/Paint.h2
-rw-r--r--libs/hwui/renderstate/OffscreenBufferPool.h2
-rw-r--r--libs/hwui/tests/common/TestScene.h2
-rw-r--r--libs/hwui/tests/common/TestUtils.h4
-rw-r--r--libs/hwui/utils/FatVector.h4
-rw-r--r--libs/hwui/utils/LinearAllocator.h6
-rw-r--r--libs/input/SpriteController.h2
-rw-r--r--media/jni/android_media_Utils.h8
-rw-r--r--media/mca/filterfw/native/core/gl_frame.h2
-rw-r--r--media/mca/filterfw/native/core/native_frame.h2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java9
-rw-r--r--services/core/Android.mk2
-rw-r--r--services/core/java/com/android/server/MountService.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java6
-rw-r--r--services/core/java/com/android/server/connectivity/MetricsLoggerService.java8
-rw-r--r--services/core/java/com/android/server/connectivity/NetdEventListenerService.java (renamed from services/core/java/com/android/server/connectivity/DnsEventListenerService.java)14
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java13
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java19
-rw-r--r--services/core/java/com/android/server/pm/AbstractStatsBase.java126
-rw-r--r--services/core/java/com/android/server/pm/CompilerStats.java294
-rw-r--r--services/core/java/com/android/server/pm/Installer.java7
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptService.java168
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptShellCommand.java18
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java28
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java339
-rw-r--r--services/core/java/com/android/server/pm/PackageUsage.java199
-rw-r--r--services/core/jni/Android.mk2
-rw-r--r--services/core/jni/com_android_server_input_InputApplicationHandle.h2
-rw-r--r--telecomm/java/android/telecom/ConnectionService.java2
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl6
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java7
-rw-r--r--wifi/java/android/net/wifi/WifiEnterpriseConfig.java89
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java47
-rw-r--r--wifi/java/android/net/wifi/WifiScanner.java87
-rw-r--r--wifi/java/android/net/wifi/nan/ConfigRequest.java4
-rw-r--r--wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl (renamed from wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl)4
-rw-r--r--wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl2
-rw-r--r--wifi/java/android/net/wifi/nan/IWifiNanManager.aidl19
-rw-r--r--wifi/java/android/net/wifi/nan/PublishConfig.java22
-rw-r--r--wifi/java/android/net/wifi/nan/SubscribeConfig.java24
-rw-r--r--wifi/java/android/net/wifi/nan/WifiNanDiscoveryBaseSession.java284
-rw-r--r--wifi/java/android/net/wifi/nan/WifiNanDiscoverySessionCallback.java (renamed from wifi/java/android/net/wifi/nan/WifiNanSessionCallback.java)67
-rw-r--r--wifi/java/android/net/wifi/nan/WifiNanEventCallback.java17
-rw-r--r--wifi/java/android/net/wifi/nan/WifiNanManager.java433
-rw-r--r--wifi/java/android/net/wifi/nan/WifiNanPublishDiscoverySession.java (renamed from wifi/java/android/net/wifi/nan/WifiNanPublishSession.java)31
-rw-r--r--wifi/java/android/net/wifi/nan/WifiNanSession.java279
-rw-r--r--wifi/java/android/net/wifi/nan/WifiNanSubscribeDiscoverySession.java (renamed from wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java)33
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_&ltT&gt&ltW&gt(&hellip;)</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);
}
}
}