diff options
45 files changed, 4345 insertions, 273 deletions
diff --git a/api/current.txt b/api/current.txt index 499676cdc2be..493d9f85feaa 100644 --- a/api/current.txt +++ b/api/current.txt @@ -54777,6 +54777,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); @@ -55091,6 +55098,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; @@ -55232,6 +55247,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); diff --git a/api/system-current.txt b/api/system-current.txt index e6f11a25f376..88c81da7fce1 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -58133,6 +58133,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); @@ -58447,6 +58454,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; @@ -58588,6 +58603,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); diff --git a/api/test-current.txt b/api/test-current.txt index f0238f90228f..08a77b96973f 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -54857,6 +54857,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); @@ -55171,6 +55178,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; @@ -55312,6 +55327,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); 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/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..e4d57184ede9 --- /dev/null +++ b/core/java/android/os/HwParcel.java @@ -0,0 +1,134 @@ +/* + * 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 writeBoolArray(int size, boolean[] val); + public native final void writeBoolVector(boolean[] val); + public native final void writeInt8Array(int size, byte[] val); + public native final void writeInt8Vector(byte[] val); + public native final void writeInt16Array(int size, short[] val); + public native final void writeInt16Vector(short[] val); + public native final void writeInt32Array(int size, int[] val); + public native final void writeInt32Vector(int[] val); + public native final void writeInt64Array(int size, long[] val); + public native final void writeInt64Vector(long[] val); + public native final void writeFloatArray(int size, float[] val); + public native final void writeFloatVector(float[] val); + public native final void writeDoubleArray(int size, double[] val); + public native final void writeDoubleVector(double[] val); + public native final void writeStringArray(int size, String[] 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[] readBoolArray(int size); + public native final boolean[] readBoolVector(); + public native final byte[] readInt8Array(int size); + public native final byte[] readInt8Vector(); + public native final short[] readInt16Array(int size); + public native final short[] readInt16Vector(); + public native final int[] readInt32Array(int size); + public native final int[] readInt32Vector(); + public native final long[] readInt64Array(int size); + public native final long[] readInt64Vector(); + public native final float[] readFloatArray(int size); + public native final float[] readFloatVector(); + public native final double[] readDoubleArray(int size); + public native final double[] readDoubleVector(); + public native final String[] readStringArray(int size); + 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/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..97833a078f4c --- /dev/null +++ b/core/jni/android_os_HwBinder.cpp @@ -0,0 +1,288 @@ +/* + * 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)), + 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))).string() + << "'"; + + sp<hardware::IBinder> service = + hardware::defaultServiceManager()->getService( + String16(reinterpret_cast<const char16_t *>(serviceName)), + 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..d453b29c0663 --- /dev/null +++ b/core/jni/android_os_HwParcel.cpp @@ -0,0 +1,1139 @@ +/* + * 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_ARRAY_WRITER(Suffix,Type) \ +static void JHwParcel_native_write ## Suffix ## Array( \ + JNIEnv *env, jobject thiz, jint size, Type ## Array valObj) { \ + if (valObj == NULL) { \ + jniThrowException(env, "java/lang/NullPointerException", NULL); \ + return; \ + } \ + \ + jsize len = env->GetArrayLength(valObj); \ + \ + if (len != size) { \ + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); \ + return; \ + } \ + \ + sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz); \ + \ + const Type *val = \ + impl->getStorage()->allocTemporary ## Suffix ## Array(env, valObj); \ + \ + hardware::Parcel *parcel = impl->getParcel(); \ + \ + size_t parentHandle; \ + status_t err = parcel->writeBuffer( \ + val, size * sizeof(*val), &parentHandle); \ + \ + 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_ARRAY_WRITER(Int8,jbyte) +DEFINE_PARCEL_ARRAY_WRITER(Int16,jshort) +DEFINE_PARCEL_ARRAY_WRITER(Int32,jint) +DEFINE_PARCEL_ARRAY_WRITER(Int64,jlong) +DEFINE_PARCEL_ARRAY_WRITER(Float,jfloat) +DEFINE_PARCEL_ARRAY_WRITER(Double,jdouble) + +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_writeBoolArray( + JNIEnv *env, jobject thiz, jint size, jbooleanArray valObj) { + if (valObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + jsize len = env->GetArrayLength(valObj); + + if (len != size) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz); + + jboolean *src = env->GetBooleanArrayElements(valObj, nullptr); + + bool *dst = + (bool *)impl->getStorage()->allocTemporaryStorage(size * sizeof(bool)); + + for (jint i = 0; i < size; ++i) { + dst[i] = src[i]; + } + + env->ReleaseBooleanArrayElements(valObj, src, 0 /* mode */); + src = nullptr; + + hardware::Parcel *parcel = impl->getParcel(); + + size_t parentHandle; + status_t err = parcel->writeBuffer( + dst, size * sizeof(*dst), &parentHandle); + + signalExceptionForError(env, err); +} + +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_ARRAY_READER(Suffix,Type,NewType) \ +static Type ## Array JHwParcel_native_read ## Suffix ## Array( \ + JNIEnv *env, jobject thiz, jint size) { \ + hardware::Parcel *parcel = \ + JHwParcel::GetNativeContext(env, thiz)->getParcel(); \ + \ + size_t parentHandle; \ + const Type *val = static_cast<const Type *>( \ + parcel->readBuffer(&parentHandle)); \ + \ + Type ## Array valObj = env->New ## NewType ## Array(size); \ + env->Set ## NewType ## ArrayRegion(valObj, 0, size, val); \ + \ + return valObj; \ +} + +#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_ARRAY_READER(Int8,jbyte,Byte) +DEFINE_PARCEL_ARRAY_READER(Int16,jshort,Short) +DEFINE_PARCEL_ARRAY_READER(Int32,jint,Int) +DEFINE_PARCEL_ARRAY_READER(Int64,jlong,Long) +DEFINE_PARCEL_ARRAY_READER(Float,jfloat,Float) +DEFINE_PARCEL_ARRAY_READER(Double,jdouble,Double) + +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_readBoolArray( + JNIEnv *env, jobject thiz, jint size) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + size_t parentHandle; + const bool *val = static_cast<const bool *>( + parcel->readBuffer(&parentHandle)); + + jbooleanArray valObj = env->NewBooleanArray(size); + + for (jint i = 0; i < size; ++i) { + jboolean x = val[i]; + env->SetBooleanArrayRegion(valObj, i, 1, &x); + } + + return valObj; +} + +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_readStringArray( + JNIEnv *env, jobject thiz, jint size) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + size_t parentHandle; + const hidl_string *val = static_cast<const hidl_string *>( + parcel->readBuffer(&parentHandle)); + + if (val == NULL) { + signalExceptionForError(env, UNKNOWN_ERROR); + return NULL; + } + + status_t err = OK; + for (jint i = 0; (err == OK) && (i < size); ++i) { + err = const_cast<hidl_string *>(&val[i]) + ->readEmbeddedFromParcel( + *parcel, + parentHandle, + i * sizeof(hidl_string)); + } + + if (err != OK) { + signalExceptionForError(env, err); + return NULL; + } + + return MakeStringArray(env, val, size); +} + +static void JHwParcel_native_writeStringArray( + JNIEnv *env, jobject thiz, jint size, jobjectArray arrayObj) { + if (arrayObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + jsize len = env->GetArrayLength(arrayObj); + + if (len != size) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz); + + hidl_string *strings = impl->getStorage()->allocStringArray(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( + strings, sizeof(hidl_string) * len, &parentHandle); + + for (jsize i = 0; (err == OK) && (i < len); ++i) { + err = strings[i].writeEmbeddedToParcel( + parcel, parentHandle, i * sizeof(hidl_string)); + } + + signalExceptionForError(env, err); +} + +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 }, + + { "writeBoolArray", "(I[Z)V", (void *)JHwParcel_native_writeBoolArray }, + { "writeBoolVector", "([Z)V", (void *)JHwParcel_native_writeBoolVector }, + { "writeInt8Array", "(I[B)V", (void *)JHwParcel_native_writeInt8Array }, + { "writeInt8Vector", "([B)V", (void *)JHwParcel_native_writeInt8Vector }, + { "writeInt16Array", "(I[S)V", (void *)JHwParcel_native_writeInt16Array }, + { "writeInt16Vector", "([S)V", (void *)JHwParcel_native_writeInt16Vector }, + { "writeInt32Array", "(I[I)V", (void *)JHwParcel_native_writeInt32Array }, + { "writeInt32Vector", "([I)V", (void *)JHwParcel_native_writeInt32Vector }, + { "writeInt64Array", "(I[J)V", (void *)JHwParcel_native_writeInt64Array }, + { "writeInt64Vector", "([J)V", (void *)JHwParcel_native_writeInt64Vector }, + { "writeFloatArray", "(I[F)V", (void *)JHwParcel_native_writeFloatArray }, + { "writeFloatVector", "([F)V", (void *)JHwParcel_native_writeFloatVector }, + { "writeDoubleArray", "(I[D)V", (void *)JHwParcel_native_writeDoubleArray }, + + { "writeDoubleVector", "([D)V", + (void *)JHwParcel_native_writeDoubleVector }, + + { "writeStringArray", "(I[Ljava/lang/String;)V", + (void *)JHwParcel_native_writeStringArray }, + + { "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 }, + + { "readBoolArray", "(I)[Z", (void *)JHwParcel_native_readBoolArray }, + { "readBoolVector", "()[Z", (void *)JHwParcel_native_readBoolVector }, + { "readInt8Array", "(I)[B", (void *)JHwParcel_native_readInt8Array }, + { "readInt8Vector", "()[B", (void *)JHwParcel_native_readInt8Vector }, + { "readInt16Array", "(I)[S", (void *)JHwParcel_native_readInt16Array }, + { "readInt16Vector", "()[S", (void *)JHwParcel_native_readInt16Vector }, + { "readInt32Array", "(I)[I", (void *)JHwParcel_native_readInt32Array }, + { "readInt32Vector", "()[I", (void *)JHwParcel_native_readInt32Vector }, + { "readInt64Array", "(I)[J", (void *)JHwParcel_native_readInt64Array }, + { "readInt64Vector", "()[J", (void *)JHwParcel_native_readInt64Vector }, + { "readFloatArray", "(I)[F", (void *)JHwParcel_native_readFloatArray }, + { "readFloatVector", "()[F", (void *)JHwParcel_native_readFloatVector }, + { "readDoubleArray", "(I)[D", (void *)JHwParcel_native_readDoubleArray }, + { "readDoubleVector", "()[D", (void *)JHwParcel_native_readDoubleVector }, + + { "readStringArray", "(I)[Ljava/lang/String;", + (void *)JHwParcel_native_readStringArray }, + + { "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/hwbinder/EphemeralStorage.cpp b/core/jni/hwbinder/EphemeralStorage.cpp new file mode 100644 index 000000000000..187beeea4de1 --- /dev/null +++ b/core/jni/hwbinder/EphemeralStorage.cpp @@ -0,0 +1,178 @@ +/* + * 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_ARRAY_METHODS(Suffix,Type,NewType) \ +const Type *EphemeralStorage::allocTemporary ## Suffix ## Array( \ + JNIEnv *env, Type ## Array arrayObj) { \ + Type ## Array obj = (Type ## Array)env->NewGlobalRef(arrayObj); \ + 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); \ + \ + return val; \ +} + +#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_ARRAY_METHODS(Int8,jbyte,Byte) +DEFINE_ALLOC_ARRAY_METHODS(Int16,jshort,Short) +DEFINE_ALLOC_ARRAY_METHODS(Int32,jint,Int) +DEFINE_ALLOC_ARRAY_METHODS(Int64,jlong,Long) +DEFINE_ALLOC_ARRAY_METHODS(Float,jfloat,Float) +DEFINE_ALLOC_ARRAY_METHODS(Double,jdouble,Double) + +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..b02ed9f5ea69 --- /dev/null +++ b/core/jni/hwbinder/EphemeralStorage.h @@ -0,0 +1,84 @@ +/* + * 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 Type *allocTemporary ## Suffix ## Array( \ + JNIEnv *env, Type ## Array arrayObj); \ + \ + 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/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk new file mode 100644 index 000000000000..a6f87ea2e5df --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk @@ -0,0 +1,38 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_SDK_VERSION := 18 + +LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp_corrupted + +LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex + +LOCAL_DEX_PREOPT := false + +include $(BUILD_PACKAGE) + +corrupted_classes2_dex := $(dir $(built_dex))/classes2.dex + +$(corrupted_classes2_dex): $(built_dex) + $(hide) touch $@ + +$(LOCAL_BUILT_MODULE): $(corrupted_classes2_dex) diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml new file mode 100644 index 000000000000..7993c6f48f83 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.framework.multidexlegacycorrupteddex" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="18" + android:targetSdkVersion="18" /> + + <application + android:name="android.support.multidex.MultiDexApplication" + android:allowBackup="true" + android:label="MultiDexLegacyTestApp_corrupted"> + <activity + android:name="com.android.framework.multidexlegacycorrupteddex.MainActivity" + android:label="MultiDexLegacyTestApp_corrupted" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.framework.multidexlegacycorrupteddex" + android:label="Test for MultiDexLegacyTestApp_corrupted" /> + +</manifest> diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/res/layout/activity_main.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/res/layout/activity_main.xml new file mode 100644 index 000000000000..58ae67a6d11e --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/res/layout/activity_main.xml @@ -0,0 +1,7 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".MainActivity" > + +</RelativeLayout> diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/CorruptedDexTest.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/CorruptedDexTest.java new file mode 100644 index 000000000000..afef394344cb --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/CorruptedDexTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.framework.multidexlegacycorrupteddex; + +import android.test.ActivityInstrumentationTestCase2; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +/** + * Run the tests with: <code>adb shell am instrument -w + com.android.framework.multidexlegacycorrupteddex/android.test.InstrumentationTestRunner +</code> + */ +public class CorruptedDexTest extends ActivityInstrumentationTestCase2<MainActivity> +{ + + public CorruptedDexTest() { + super(MainActivity.class); + } + + /** + * Tests that when a {@link ClassNotFoundException} is thrown, the message also contains + * something about the suppressed IOException. + */ + public void testSupressedExceptions() + { + try { + Class.forName("notapackage.NotAClass"); + throw new AssertionError(); + } catch (ClassNotFoundException e) { + // expected + +// This the check we should do but API is not yet available in 19 +// Throwable[] suppressed = e.getSuppressed(); +// assertTrue(suppressed.length > 0); +// boolean ioFound = false; +// for (Throwable throwable : suppressed) { +// if (throwable instanceof IOException) { +// ioFound = true; +// break; +// } +// } +// assertTrue(ioFound); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(baos); + e.printStackTrace(ps); + ps.close(); + assertTrue("Exception message should mention IOException but is not: " + + baos.toString(), + baos.toString().contains("IOException")); + } + } +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/MainActivity.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/MainActivity.java new file mode 100644 index 000000000000..61ba5b866499 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/MainActivity.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.framework.multidexlegacycorrupteddex; + +import android.app.Activity; +import android.os.Bundle; + +public class MainActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + } + +} diff --git a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java index da8bc1d0945d..7935880d12a9 100644 --- a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java @@ -45,6 +45,7 @@ public class WakeupMessageTest { private static final int TEST_CMD = 18; private static final int TEST_ARG1 = 33; private static final int TEST_ARG2 = 182; + private static final Object TEST_OBJ = "hello"; @Mock AlarmManager mAlarmManager; WakeupMessage mMessage; @@ -92,7 +93,7 @@ public class WakeupMessageTest { mListenerCaptor.capture(), any(Handler.class)); mMessage = new WakeupMessage(context, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1, - TEST_ARG2); + TEST_ARG2, TEST_OBJ); } /** @@ -114,6 +115,7 @@ public class WakeupMessageTest { assertEquals("what", TEST_CMD, mHandler.getLastMessage().what); assertEquals("arg1", TEST_ARG1, mHandler.getLastMessage().arg1); assertEquals("arg2", TEST_ARG2, mHandler.getLastMessage().arg2); + assertEquals("obj", TEST_OBJ, mHandler.getLastMessage().obj); } /** diff --git a/docs/html/guide/topics/renderscript/compute.jd b/docs/html/guide/topics/renderscript/compute.jd index c5b49d70435f..b30c74104bc1 100755 --- a/docs/html/guide/topics/renderscript/compute.jd +++ b/docs/html/guide/topics/renderscript/compute.jd @@ -10,12 +10,13 @@ parent.link=index.html <ol> <li><a href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a></li> - <li><a href="#access-rs-apis">Accessing RenderScript APIs</a> + <li><a href="#access-rs-apis">Accessing RenderScript APIs from Java</a> <ol> <li><a href="#ide-setup">Setting Up Your Development Environment</a></li> </ol> </li> <li><a href="#using-rs-from-java">Using RenderScript from Java Code</a></li> + <li><a href="#single-source-rs">Single-Source RenderScript</a></li> <li><a href="#reduction-in-depth">Reduction Kernels in Depth</a> <ol> <li><a href="#writing-reduction-kernel">Writing a reduction kernel</a></li> @@ -45,12 +46,16 @@ computer vision.</p> <p>To begin with RenderScript, there are two main concepts you should understand:</p> <ul> -<li>High-performance compute kernels are written in a C99-derived language. A <i>compute - kernel</i> is a function or collection of functions that you can direct the RenderScript runtime - to execute in parallel across a collection of data.</li> +<li>The <em>language</em> itself is a C99-derived language for writing high-performance compute +code. <a href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a> describes +how to use it to write compute kernels.</li> -<li>A Java API is used for managing the lifetime of RenderScript resources and controlling kernel -execution.</li> +<li>The <em>control API</em> is used for managing the lifetime of RenderScript resources and +controlling kernel execution. It is available in three different languages: Java, C++ in Android +NDK, and the C99-derived kernel language itself. +<a href="#using-rs-from-java">Using RenderScript from Java Code</a> and +<a href=#single-source-rs>Single-Source RenderScript</a> describe the first and the third +options, respectively.</li> </ul> <h2 id="writing-an-rs-kernel">Writing a RenderScript Kernel</h2> @@ -77,7 +82,9 @@ initial setup or serial computations within a larger processing pipeline.</li> access script globals from Java code, and these are often used for parameter passing to RenderScript kernels.</p></li> -<li><p>Zero or more <strong><i>compute kernels</i></strong>. There are two kinds of compute +<li><p>Zero or more <strong><i>compute kernels</i></strong>. A compute kernel is a function +or collection of functions that you can direct the RenderScript runtime to execute in parallel +across a collection of data. There are two kinds of compute kernels: <i>mapping</i> kernels (also called <i>foreach</i> kernels) and <i>reduction</i> kernels.</p> @@ -243,9 +250,9 @@ beneficial on some architectures due to additional optimizations only available precision (such as SIMD CPU instructions).</p> -<h2 id="access-rs-apis">Accessing RenderScript APIs</h2> +<h2 id="access-rs-apis">Accessing RenderScript APIs from Java</h2> -<p>When developing an Android application that uses RenderScript, you can access its API in +<p>When developing an Android application that uses RenderScript, you can access its API from Java in one of two ways:</p> <ul> @@ -377,7 +384,7 @@ to you when using RenderScript: <ul> <li><strong>ScriptC</strong>: These are the user-defined scripts as described in <a -href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a> above. Every script has a Java class +href="#writing-an-rs-kernel"><i>Writing a RenderScript Kernel</i></a> above. Every script has a Java class reflected by the RenderScript compiler in order to make it easy to access the script from Java code; this class has the name <code>ScriptC_<i>filename</i></code>. For example, if the mapping kernel above were located in <code>invert.rs</code> and a RenderScript context were already located in @@ -448,6 +455,116 @@ context to throw an exception.</li> </ol> a <code>get()</code> method to obtain the result of a reduction. <code>get()</code> is synchronous, and is serialized with respect to the reduction (which is asynchronous).</p> +<h2 id="single-source-rs">Single-Source RenderScript</h2> + +<p>Android 7.0 (API level 24) introduces a new programming feature called <em>Single-Source +RenderScript</em>, in which kernels are launched from the script where they are defined, rather than +from Java. This approach is currently limited to mapping kernels, which are simply referred to as "kernels" +in this section for conciseness. This new feature also supports creating allocations of type +<a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_allocation> +<code>rs_allocation</code></a> from inside the script. It is now possible to +implement a whole algorithm solely within a script, even if multiple kernel launches are required. +The benefit is twofold: more readable code, because it keeps the implementation of an algorithm in +one language; and potentially faster code, because of fewer transitions between Java and +RenderScript across multiple kernel launches.</p> + +<p>In Single-Source RenderScript, you write kernels as described in <a href="#writing-an-rs-kernel"> +Writing a RenderScript Kernel</a>. You then write an invokable function that calls +<a href="{@docRoot}guide/topics/renderscript/reference/rs_for_each.html#android_rs:rsForEach"> +<code>rsForEach()</code></a> to launch them. That API takes a kernel function as the first +parameter, followed by input and output allocations. A similar API +<a href="{@docRoot}guide/topics/renderscript/reference/rs_for_each.html#android_rs:rsForEachWithOptions"> +<code>rsForEachWithOptions()</code></a> takes an extra argument of type +<a href="{@docRoot}guide/topics/renderscript/reference/rs_for_each.html#android_rs:rs_script_call_t"> +<code>rs_script_call_t</code></a>, which specifies a subset of the elements from the input and +output allocations for the kernel function to process.</p> + +<p>To start RenderScript computation, you call the invokable function from Java. +Follow the steps in <a href="#using-rs-from-java">Using RenderScript from Java Code</a>. +In the step <a href="#launching_kernels">launch the appropriate kernels</a>, call +the invokable function using <code>invoke_<i>function_name</i>()</code>, which will start the +whole computation, including launching kernels.</p> + +<p>Allocations are often needed to save and pass +intermediate results from one kernel launch to another. You can create them using +<a href="{@docRoot}guide/topics/renderscript/reference/rs_allocation_create.html#android_rs:rsCreateAllocation"> +rsCreateAllocation()</a>. One easy-to-use form of that API is <code> +rsCreateAllocation_<T><W>(…)</code>, where <i>T</i> is the data type for an +element, and <i>W</i> is the vector width for the element. The API takes the sizes in +dimensions X, Y, and Z as arguments. For 1D or 2D allocations, the size for dimension Y or Z can +be omitted. For example, <code>rsCreateAllocation_uchar4(16384)</code> creates a 1D allocation of +16384 elements, each of which is of type <code>uchar4</code>.</p> + +<p>Allocations are managed by the system automatically. You +do not have to explicitly release or free them. However, you can call +<a href="{@docRoot}guide/topics/renderscript/reference/rs_object_info.html#android_rs:rsClearObject"> +<code>rsClearObject(rs_allocation* alloc)</code></a> to indicate you no longer need the handle +<code>alloc</code> to the underlying allocation, +so that the system can free up resources as early as possible.</p> + +<p>The <a href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a> section contains an example +kernel that inverts an image. The example below expands that to apply more than one effect to an image, +using Single-Source RenderScript. It includes another kernel, <code>greyscale</code>, which turns a +color image into black-and-white. An invokable function <code>process()</code> then applies those two kernels +consecutively to an input image, and produces an output image. Allocations for both the input and +the output are passed in as arguments of type +<a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_allocation> +<code>rs_allocation</code></a>.</p> + +<pre> +// File: singlesource.rs + +#pragma version(1) +#pragma rs java_package_name(com.android.rssample) + +static const float4 weight = {0.299f, 0.587f, 0.114f, 0.0f}; + +uchar4 RS_KERNEL invert(uchar4 in, uint32_t x, uint32_t y) { + uchar4 out = in; + out.r = 255 - in.r; + out.g = 255 - in.g; + out.b = 255 - in.b; + return out; +} + +uchar4 RS_KERNEL greyscale(uchar4 in) { + const float4 inF = rsUnpackColor8888(in); + const float4 outF = (float4){ dot(inF, weight) }; + return rsPackColorTo8888(outF); +} + +void process(rs_allocation inputImage, rs_allocation outputImage) { + const uint32_t imageWidth = rsAllocationGetDimX(inputImage); + const uint32_t imageHeight = rsAllocationGetDimY(inputImage); + rs_allocation tmp = rsCreateAllocation_uchar4(imageWidth, imageHeight); + rsForEach(invert, inputImage, tmp); + rsForEach(greyscale, tmp, outputImage); +} +</pre> + +<p>You can call the <code>process()</code> function from Java as follows:</p> + +<pre> +// File SingleSource.java + +RenderScript RS = RenderScript.create(context); +ScriptC_singlesource script = new ScriptC_singlesource(RS); +Allocation inputAllocation = Allocation.createFromBitmapResource( + RS, getResources(), R.drawable.image); +Allocation outputAllocation = Allocation.createTyped( + RS, inputAllocation.getType(), + Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_OUTPUT); +script.invoke_process(inputAllocation, outputAllocation); +</pre> + +<p>This example shows how an algorithm that involves two kernel launches can be implemented completely +in the RenderScript language itself. Without Single-Source +RenderScript, you would have to launch both kernels from the Java code, separating kernel launches +from kernel definitions and making it harder to understand the whole algorithm. Not only is the +Single-Source RenderScript code easier to read, it also eliminates the transitioning +between Java and the script across kernel launches. Some iterative algorithms may launch kernels +hundreds of times, making the overhead of such transitioning considerable.</p> + <h2 id="reduction-in-depth">Reduction Kernels in Depth</h2> <p><i>Reduction</i> is the process of combining a collection of data into a single diff --git a/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/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/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/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/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..227230646250 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -155,6 +155,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; } |