diff options
315 files changed, 8889 insertions, 1666 deletions
diff --git a/Android.mk b/Android.mk index c5b3b6ba0e3d..b644a179a871 100644 --- a/Android.mk +++ b/Android.mk @@ -1194,20 +1194,45 @@ LOCAL_MODULE := ds LOCAL_DROIDDOC_OPTIONS:= \ $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ - -devsite \ - -hdf devsite true \ -toroot / \ -hdf android.whichdoc online \ + -devsite \ $(sample_groups) \ - -useUpdatedTemplates \ -hdf android.hasSamples true \ - -yaml _book.yaml \ -samplesdir $(samples_dir) LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk-dev include $(BUILD_DROIDDOC) +# ==== docs for the web (on the devsite app engine server) ======================= +include $(CLEAR_VARS) +LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES) +LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES) +LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES) +LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES) +LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS) +LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH) +LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR) +LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR) +LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) +# specify a second html input dir and an output path relative to OUT_DIR) +LOCAL_ADDITIONAL_HTML_DIR:=docs/html-intl / + +LOCAL_MODULE := ds-static + +LOCAL_DROIDDOC_OPTIONS:= \ + $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ + -hdf android.whichdoc online \ + -staticonly \ + -toroot / \ + -devsite \ + -ignoreJdLinks + +LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk-dev + +include $(BUILD_DROIDDOC) + # ==== site updates for docs (on the androiddevdocs app engine server) ======================= include $(CLEAR_VARS) diff --git a/api/current.txt b/api/current.txt index d496bb8492ed..5b230eea2435 100644 --- a/api/current.txt +++ b/api/current.txt @@ -43176,6 +43176,7 @@ package android.view { method public int getScaledOverscrollDistance(); method public int getScaledPagingTouchSlop(); method public int getScaledScrollBarSize(); + method public int getScaledScrollFactor(); method public int getScaledTouchSlop(); method public int getScaledWindowTouchSlop(); method public static int getScrollBarFadeDuration(); @@ -43999,6 +44000,7 @@ package android.view { field public static final int TYPE_APPLICATION_SUB_PANEL = 1002; // 0x3ea field public static final int TYPE_BASE_APPLICATION = 1; // 0x1 field public static final int TYPE_CHANGED = 2; // 0x2 + field public static final int TYPE_DRAWN_APPLICATION = 4; // 0x4 field public static final int TYPE_INPUT_METHOD = 2011; // 0x7db field public static final int TYPE_INPUT_METHOD_DIALOG = 2012; // 0x7dc field public static final int TYPE_KEYGUARD_DIALOG = 2009; // 0x7d9 @@ -59027,9 +59029,17 @@ package java.util { ctor public HashMap(); ctor public HashMap(java.util.Map<? extends K, ? extends V>); method public java.lang.Object clone(); + method public V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>); + method public V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>); + method public V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>); method public java.util.Set<java.util.Map.Entry<K, V>> entrySet(); method public void forEach(java.util.function.BiConsumer<? super K, ? super V>); + method public V getOrDefault(java.lang.Object, V); + method public V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>); + method public V putIfAbsent(K, V); + method public boolean remove(java.lang.Object, java.lang.Object); method public boolean replace(K, V, V); + method public V replace(K, V); method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>); } diff --git a/api/removed.txt b/api/removed.txt index 94ba45244b9f..239eab657008 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -179,6 +179,10 @@ package android.os { ctor public BatteryManager(); } + public class Build { + field public static final boolean PERMISSIONS_REVIEW_REQUIRED; + } + public final class PowerManager { method public void goToSleep(long); method public deprecated void userActivity(long, boolean); diff --git a/api/system-current.txt b/api/system-current.txt index 6db91c61ee4e..2996be73e332 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -46346,6 +46346,7 @@ package android.view { method public int getScaledOverscrollDistance(); method public int getScaledPagingTouchSlop(); method public int getScaledScrollBarSize(); + method public int getScaledScrollFactor(); method public int getScaledTouchSlop(); method public int getScaledWindowTouchSlop(); method public static int getScrollBarFadeDuration(); @@ -47172,6 +47173,7 @@ package android.view { field public static final int TYPE_APPLICATION_SUB_PANEL = 1002; // 0x3ea field public static final int TYPE_BASE_APPLICATION = 1; // 0x1 field public static final int TYPE_CHANGED = 2; // 0x2 + field public static final int TYPE_DRAWN_APPLICATION = 4; // 0x4 field public static final int TYPE_INPUT_METHOD = 2011; // 0x7db field public static final int TYPE_INPUT_METHOD_DIALOG = 2012; // 0x7dc field public static final int TYPE_KEYGUARD_DIALOG = 2009; // 0x7d9 @@ -62554,9 +62556,17 @@ package java.util { ctor public HashMap(); ctor public HashMap(java.util.Map<? extends K, ? extends V>); method public java.lang.Object clone(); + method public V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>); + method public V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>); + method public V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>); method public java.util.Set<java.util.Map.Entry<K, V>> entrySet(); method public void forEach(java.util.function.BiConsumer<? super K, ? super V>); + method public V getOrDefault(java.lang.Object, V); + method public V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>); + method public V putIfAbsent(K, V); + method public boolean remove(java.lang.Object, java.lang.Object); method public boolean replace(K, V, V); + method public V replace(K, V); method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>); } diff --git a/api/test-current.txt b/api/test-current.txt index 5293a04acf7d..52554691972c 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -43257,6 +43257,7 @@ package android.view { method public int getScaledOverscrollDistance(); method public int getScaledPagingTouchSlop(); method public int getScaledScrollBarSize(); + method public int getScaledScrollFactor(); method public int getScaledTouchSlop(); method public int getScaledWindowTouchSlop(); method public static int getScrollBarFadeDuration(); @@ -44080,6 +44081,7 @@ package android.view { field public static final int TYPE_APPLICATION_SUB_PANEL = 1002; // 0x3ea field public static final int TYPE_BASE_APPLICATION = 1; // 0x1 field public static final int TYPE_CHANGED = 2; // 0x2 + field public static final int TYPE_DRAWN_APPLICATION = 4; // 0x4 field public static final int TYPE_INPUT_METHOD = 2011; // 0x7db field public static final int TYPE_INPUT_METHOD_DIALOG = 2012; // 0x7dc field public static final int TYPE_KEYGUARD_DIALOG = 2009; // 0x7d9 @@ -59116,9 +59118,17 @@ package java.util { ctor public HashMap(); ctor public HashMap(java.util.Map<? extends K, ? extends V>); method public java.lang.Object clone(); + method public V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>); + method public V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>); + method public V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>); method public java.util.Set<java.util.Map.Entry<K, V>> entrySet(); method public void forEach(java.util.function.BiConsumer<? super K, ? super V>); + method public V getOrDefault(java.lang.Object, V); + method public V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>); + method public V putIfAbsent(K, V); + method public boolean remove(java.lang.Object, java.lang.Object); method public boolean replace(K, V, V); + method public V replace(K, V); method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>); } diff --git a/api/test-removed.txt b/api/test-removed.txt index 94ba45244b9f..239eab657008 100644 --- a/api/test-removed.txt +++ b/api/test-removed.txt @@ -179,6 +179,10 @@ package android.os { ctor public BatteryManager(); } + public class Build { + field public static final boolean PERMISSIONS_REVIEW_REQUIRED; + } + public final class PowerManager { method public void goToSleep(long); method public deprecated void userActivity(long, boolean); diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 72a21e326564..80af5ea9fdaf 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -17,7 +17,6 @@ #include <binder/ProcessState.h> #include <utils/Log.h> #include <cutils/memory.h> -#include <cutils/process_name.h> #include <cutils/properties.h> #include <cutils/trace.h> #include <android_runtime/AndroidRuntime.h> diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java index 221923821bbb..aef1d0c31f9d 100644 --- a/core/java/android/app/ActivityTransitionState.java +++ b/core/java/android/app/ActivityTransitionState.java @@ -337,11 +337,12 @@ class ActivityTransitionState { } public void startExitOutTransition(Activity activity, Bundle options) { - if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) { + mEnterTransitionCoordinator = null; + if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) || + mExitTransitionCoordinators == null) { return; } ActivityOptions activityOptions = new ActivityOptions(options); - mEnterTransitionCoordinator = null; if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { int key = activityOptions.getExitCoordinatorKey(); int index = mExitTransitionCoordinators.indexOfKey(key); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 6ac7132166f7..593432e9c0c7 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2341,8 +2341,10 @@ public class DevicePolicyManager { public static final int WIPE_RESET_PROTECTION_DATA = 0x0002; /** - * Ask the user data be wiped. Wiping the primary user will cause the device to reboot, erasing - * all user data while next booting up. + * Ask that all user data be wiped. If called as a secondary user, the user will be removed and + * other users will remain unaffected. Calling from the primary user will cause the device to + * reboot, erasing all device data - including all the secondary users and their data - while + * booting up. * <p> * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to * be able to call this method; if it has not, a security exception will be thrown. diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java index 953b051ae6da..f47ad1fb03e9 100644 --- a/core/java/android/content/pm/IntentFilterVerificationInfo.java +++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java @@ -60,9 +60,9 @@ public final class IntentFilterVerificationInfo implements Parcelable { mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; } - public IntentFilterVerificationInfo(String packageName, ArrayList<String> domains) { + public IntentFilterVerificationInfo(String packageName, ArraySet<String> domains) { mPackageName = packageName; - mDomains.addAll(domains); + mDomains = domains; mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; } @@ -96,8 +96,8 @@ public final class IntentFilterVerificationInfo implements Parcelable { return mDomains; } - public void setDomains(ArrayList<String> list) { - mDomains = new ArraySet<>(list); + public void setDomains(ArraySet<String> list) { + mDomains = list; } public String getDomainsString() { diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 8f62d3ff0f9b..766963593f32 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2595,7 +2595,8 @@ public class ConnectivityManager { /** * Called if no network is found in the given timeout time. If no timeout is given, - * this will not be called. + * this will not be called. The associated {@link NetworkRequest} will have already + * been removed and released, as if {@link #unregisterNetworkCallback} had been called. * @hide */ public void onUnavailable() {} @@ -2668,6 +2669,26 @@ public class ConnectivityManager { /** @hide */ public static final int CALLBACK_RESUMED = BASE + 12; + /** @hide */ + public static String getCallbackName(int whichCallback) { + switch (whichCallback) { + case CALLBACK_PRECHECK: return "CALLBACK_PRECHECK"; + case CALLBACK_AVAILABLE: return "CALLBACK_AVAILABLE"; + case CALLBACK_LOSING: return "CALLBACK_LOSING"; + case CALLBACK_LOST: return "CALLBACK_LOST"; + case CALLBACK_UNAVAIL: return "CALLBACK_UNAVAIL"; + case CALLBACK_CAP_CHANGED: return "CALLBACK_CAP_CHANGED"; + case CALLBACK_IP_CHANGED: return "CALLBACK_IP_CHANGED"; + case CALLBACK_RELEASED: return "CALLBACK_RELEASED"; + case CALLBACK_EXIT: return "CALLBACK_EXIT"; + case EXPIRE_LEGACY_REQUEST: return "EXPIRE_LEGACY_REQUEST"; + case CALLBACK_SUSPENDED: return "CALLBACK_SUSPENDED"; + case CALLBACK_RESUMED: return "CALLBACK_RESUMED"; + default: + return Integer.toString(whichCallback); + } + } + private class CallbackHandler extends Handler { private final HashMap<NetworkRequest, NetworkCallback>mCallbackMap; private final AtomicInteger mRefCount; @@ -2834,7 +2855,7 @@ public class ConnectivityManager { private final static int REQUEST = 2; private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, - NetworkCallback networkCallback, int timeoutSec, int action, + NetworkCallback networkCallback, int timeoutMs, int action, int legacyType) { if (networkCallback == null) { throw new IllegalArgumentException("null NetworkCallback"); @@ -2850,7 +2871,7 @@ public class ConnectivityManager { new Messenger(sCallbackHandler), new Binder()); } else { networkCallback.networkRequest = mService.requestNetwork(need, - new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType); + new Messenger(sCallbackHandler), timeoutMs, new Binder(), legacyType); } if (networkCallback.networkRequest != null) { sNetworkCallback.put(networkCallback.networkRequest, networkCallback); diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 25806fa77674..f65a50f02df3 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -904,7 +904,8 @@ public class NetworkStats implements Parcelable { if (pool.isEmpty()) { return true; } - Entry moved = addTrafficToApplications(tunIface, underlyingIface, tunIfaceTotal, pool); + Entry moved = + addTrafficToApplications(tunUid, tunIface, underlyingIface, tunIfaceTotal, pool); deductTrafficFromVpnApp(tunUid, underlyingIface, moved); if (!moved.isEmpty()) { @@ -919,9 +920,9 @@ public class NetworkStats implements Parcelable { * Initializes the data used by the migrateTun() method. * * This is the first pass iteration which does the following work: - * (1) Adds up all the traffic through tun0. - * (2) Adds up all the traffic through the tunUid's underlyingIface + * (1) Adds up all the traffic through the tunUid's underlyingIface * (both foreground and background). + * (2) Adds up all the traffic through tun0 excluding traffic from the vpn app itself. */ private void tunAdjustmentInit(int tunUid, String tunIface, String underlyingIface, Entry tunIfaceTotal, Entry underlyingIfaceTotal) { @@ -941,8 +942,9 @@ public class NetworkStats implements Parcelable { underlyingIfaceTotal.add(recycle); } - if (recycle.tag == TAG_NONE && Objects.equals(tunIface, recycle.iface)) { - // Add up all tunIface traffic. + if (recycle.uid != tunUid && recycle.tag == TAG_NONE + && Objects.equals(tunIface, recycle.iface)) { + // Add up all tunIface traffic excluding traffic from the vpn app itself. tunIfaceTotal.add(recycle); } } @@ -958,13 +960,15 @@ public class NetworkStats implements Parcelable { return pool; } - private Entry addTrafficToApplications(String tunIface, String underlyingIface, + private Entry addTrafficToApplications(int tunUid, String tunIface, String underlyingIface, Entry tunIfaceTotal, Entry pool) { Entry moved = new Entry(); Entry tmpEntry = new Entry(); tmpEntry.iface = underlyingIface; for (int i = 0; i < size; i++) { - if (Objects.equals(iface[i], tunIface)) { + // the vpn app is excluded from the redistribution but all moved traffic will be + // deducted from the vpn app (see deductTrafficFromVpnApp below). + if (Objects.equals(iface[i], tunIface) && uid[i] != tunUid) { if (tunIfaceTotal.rxBytes > 0) { tmpEntry.rxBytes = pool.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes; } else { diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 91fe3524e1ad..5b51002120d6 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -714,7 +714,7 @@ public class Build { public static final int N = 24; /** - * N MR1: Still ¯\_(シ)_/¯. + * N MR1: Nougat++. */ public static final int N_MR1 = 25; } @@ -847,6 +847,7 @@ public class Build { * is installed. * * @hide + * @removed */ @SystemApi public static final boolean PERMISSIONS_REVIEW_REQUIRED = 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/HwParcel.java b/core/java/android/os/HwParcel.java new file mode 100644 index 000000000000..fe7cdccd3e09 --- /dev/null +++ b/core/java/android/os/HwParcel.java @@ -0,0 +1,120 @@ +/* + * 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 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 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 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 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(); + + public native final void writeStatus(int status); + public native final void verifySuccess(); + public native final void releaseTemporaryStorage(); + + public native final void send(); + + // Returns address of the "freeFunction". + private static native final long native_init(); + + private native final void native_setup(boolean allocate); + + static { + long freeFunction = native_init(); + + sNativeRegistry = new NativeAllocationRegistry( + HwParcel.class.getClassLoader(), + freeFunction, + 128 /* size */); + } + + private long mNativeContext; +} + diff --git a/core/java/android/os/HwRemoteBinder.java b/core/java/android/os/HwRemoteBinder.java new file mode 100644 index 000000000000..83866b3cceb7 --- /dev/null +++ b/core/java/android/os/HwRemoteBinder.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import libcore.util.NativeAllocationRegistry; + +/** @hide */ +public class HwRemoteBinder implements IHwBinder { + private static final String TAG = "HwRemoteBinder"; + + private static final NativeAllocationRegistry sNativeRegistry; + + public HwRemoteBinder() { + native_setup_empty(); + + sNativeRegistry.registerNativeAllocation( + this, + mNativeContext); + } + + public IHwInterface queryLocalInterface(String descriptor) { + return null; + } + + public native final void transact( + int code, HwParcel request, HwParcel reply, int flags); + + private static native final long native_init(); + + private native final void native_setup_empty(); + + static { + long freeFunction = native_init(); + + sNativeRegistry = new NativeAllocationRegistry( + HwRemoteBinder.class.getClassLoader(), + freeFunction, + 128 /* size */); + } + + private long mNativeContext; +} diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java new file mode 100644 index 000000000000..76e881eda8af --- /dev/null +++ b/core/java/android/os/IHwBinder.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** @hide */ +public interface IHwBinder { + // These MUST match their corresponding libhwbinder/IBinder.h definition !!! + public static final int FIRST_CALL_TRANSACTION = 1; + public static final int FLAG_ONEWAY = 1; + + public void transact( + int code, HwParcel request, HwParcel reply, int flags); + + public IHwInterface queryLocalInterface(String descriptor); +} diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java new file mode 100644 index 000000000000..7c5ac6f44a49 --- /dev/null +++ b/core/java/android/os/IHwInterface.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** @hide */ +public interface IHwInterface { + public IHwBinder asBinder(); +} diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index d7a72960f4c4..f050d7675562 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -237,6 +237,15 @@ public class ZygoteProcess { ZygoteState zygoteState, ArrayList<String> args) throws ZygoteStartFailedEx { try { + // Throw early if any of the arguments are malformed. This means we can + // avoid writing a partial response to the zygote. + int sz = args.size(); + for (int i = 0; i < sz; i++) { + if (args.get(i).indexOf('\n') >= 0) { + throw new ZygoteStartFailedEx("embedded newlines not allowed"); + } + } + /** * See com.android.internal.os.SystemZygoteInit.readArgumentList() * Presently the wire format to the zygote process is: @@ -253,13 +262,8 @@ public class ZygoteProcess { writer.write(Integer.toString(args.size())); writer.newLine(); - int sz = args.size(); for (int i = 0; i < sz; i++) { String arg = args.get(i); - if (arg.indexOf('\n') >= 0) { - throw new ZygoteStartFailedEx( - "embedded newlines not allowed"); - } writer.write(arg); writer.newLine(); } @@ -268,11 +272,16 @@ public class ZygoteProcess { // Should there be a timeout on this? Process.ProcessStartResult result = new Process.ProcessStartResult(); + + // Always read the entire result from the input stream to avoid leaving + // bytes in the stream for future process starts to accidentally stumble + // upon. result.pid = inputStream.readInt(); + result.usingWrapper = inputStream.readBoolean(); + if (result.pid < 0) { throw new ZygoteStartFailedEx("fork() failed"); } - result.usingWrapper = inputStream.readBoolean(); return result; } catch (IOException ex) { zygoteState.close(); diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java index 3855ff3ae44b..24c119f29d71 100644 --- a/core/java/android/text/method/LinkMovementMethod.java +++ b/core/java/android/text/method/LinkMovementMethod.java @@ -29,6 +29,9 @@ import android.widget.TextView; /** * A movement method that traverses links in the text buffer and scrolls if necessary. * Supports clicking on links with DPad Center or Enter. + * + * <p>Note: Starting from Android 8.0 (API level 25) this class no longer handles the touch + * clicks. */ public class LinkMovementMethod extends ScrollingMovementMethod { private static final int CLICK = 1; @@ -98,14 +101,14 @@ public class LinkMovementMethod extends ScrollingMovementMethod { int padding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom(); - int areatop = widget.getScrollY(); - int areabot = areatop + widget.getHeight() - padding; + int areaTop = widget.getScrollY(); + int areaBot = areaTop + widget.getHeight() - padding; - int linetop = layout.getLineForVertical(areatop); - int linebot = layout.getLineForVertical(areabot); + int lineTop = layout.getLineForVertical(areaTop); + int lineBot = layout.getLineForVertical(areaBot); - int first = layout.getLineStart(linetop); - int last = layout.getLineEnd(linebot); + int first = layout.getLineStart(lineTop); + int last = layout.getLineEnd(lineBot); ClickableSpan[] candidates = buffer.getSpans(first, last, ClickableSpan.class); @@ -141,46 +144,46 @@ public class LinkMovementMethod extends ScrollingMovementMethod { break; case UP: - int beststart, bestend; + int bestStart, bestEnd; - beststart = -1; - bestend = -1; + bestStart = -1; + bestEnd = -1; for (int i = 0; i < candidates.length; i++) { int end = buffer.getSpanEnd(candidates[i]); if (end < selEnd || selStart == selEnd) { - if (end > bestend) { - beststart = buffer.getSpanStart(candidates[i]); - bestend = end; + if (end > bestEnd) { + bestStart = buffer.getSpanStart(candidates[i]); + bestEnd = end; } } } - if (beststart >= 0) { - Selection.setSelection(buffer, bestend, beststart); + if (bestStart >= 0) { + Selection.setSelection(buffer, bestEnd, bestStart); return true; } break; case DOWN: - beststart = Integer.MAX_VALUE; - bestend = Integer.MAX_VALUE; + bestStart = Integer.MAX_VALUE; + bestEnd = Integer.MAX_VALUE; for (int i = 0; i < candidates.length; i++) { int start = buffer.getSpanStart(candidates[i]); if (start > selStart || selStart == selEnd) { - if (start < beststart) { - beststart = start; - bestend = buffer.getSpanEnd(candidates[i]); + if (start < bestStart) { + bestStart = start; + bestEnd = buffer.getSpanEnd(candidates[i]); } } } - if (bestend < Integer.MAX_VALUE) { - Selection.setSelection(buffer, beststart, bestend); + if (bestEnd < Integer.MAX_VALUE) { + Selection.setSelection(buffer, bestStart, bestEnd); return true; } @@ -195,8 +198,7 @@ public class LinkMovementMethod extends ScrollingMovementMethod { MotionEvent event) { int action = event.getAction(); - if (action == MotionEvent.ACTION_UP || - action == MotionEvent.ACTION_DOWN) { + if (action == MotionEvent.ACTION_DOWN) { int x = (int) event.getX(); int y = (int) event.getY(); @@ -210,17 +212,12 @@ public class LinkMovementMethod extends ScrollingMovementMethod { int line = layout.getLineForVertical(y); int off = layout.getOffsetForHorizontal(line, x); - ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); - - if (link.length != 0) { - if (action == MotionEvent.ACTION_UP) { - link[0].onClick(widget); - } else if (action == MotionEvent.ACTION_DOWN) { - Selection.setSelection(buffer, - buffer.getSpanStart(link[0]), - buffer.getSpanEnd(link[0])); - } + ClickableSpan[] links = buffer.getSpans(off, off, ClickableSpan.class); + if (links.length != 0) { + Selection.setSelection(buffer, + buffer.getSpanStart(links[0]), + buffer.getSpanEnd(links[0])); return true; } else { Selection.removeSelection(buffer); diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java index bb771000b343..7e195625e9a4 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -379,9 +379,9 @@ public class TextureView extends View { if (createNewSurface) { // Create a new SurfaceTexture for the layer. mSurface = new SurfaceTexture(false); - mLayer.setSurfaceTexture(mSurface); nCreateNativeWindow(mSurface); } + mLayer.setSurfaceTexture(mSurface); mSurface.setDefaultBufferSize(getWidth(), getHeight()); mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 5a7f0ff08782..5664f634827f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -830,7 +830,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ protected static boolean sPreserveMarginParamsInLayoutParamConversion; - /** * This view does not want keystrokes. Use with TAKES_FOCUS_MASK when * calling setFlags. @@ -9856,6 +9855,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Tells whether the {@link View} is in the state between {@link #onStartTemporaryDetach()} + * and {@link #onFinishTemporaryDetach()}. + * + * <p>This method always returns {@code true} when called directly or indirectly from + * {@link #onStartTemporaryDetach()}. The return value when called directly or indirectly from + * {@link #onFinishTemporaryDetach()}, however, depends on the OS version. + * <ul> + * <li>{@code true} on {@link android.os.Build.VERSION_CODES#N API 24}</li> + * <li>{@code false} on {@link android.os.Build.VERSION_CODES#N_MR1 API 25}} and later</li> + * </ul> + * </p> + * * @return {@code true} when the View is in the state between {@link #onStartTemporaryDetach()} * and {@link #onFinishTemporaryDetach()}. */ @@ -9890,8 +9901,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @CallSuper public void dispatchFinishTemporaryDetach() { - onFinishTemporaryDetach(); mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH; + onFinishTemporaryDetach(); if (hasWindowFocus() && hasFocus()) { InputMethodManager.getInstance().focusIn(this); } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 9a73d0b1bfaa..33b488fbc6b4 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -219,6 +219,12 @@ public class ViewConfiguration { private static final int OVERFLING_DISTANCE = 6; /** + * Amount to scroll in response to a {@link MotionEvent#ACTION_SCROLL} event, in dips per + * axis value. + */ + private static final int SCROLL_FACTOR = 64; + + /** * Default duration to hide an action mode for. */ private static final long ACTION_MODE_HIDE_DURATION_DEFAULT = 2000; @@ -246,6 +252,7 @@ public class ViewConfiguration { private final int mOverflingDistance; private final boolean mFadingMarqueeEnabled; private final long mGlobalActionsKeyTimeout; + private final int mScrollFactor; private boolean sHasPermanentMenuKey; private boolean sHasPermanentMenuKeySet; @@ -274,6 +281,7 @@ public class ViewConfiguration { mOverflingDistance = OVERFLING_DISTANCE; mFadingMarqueeEnabled = true; mGlobalActionsKeyTimeout = GLOBAL_ACTIONS_KEY_TIMEOUT; + mScrollFactor = SCROLL_FACTOR; } /** @@ -357,6 +365,8 @@ public class ViewConfiguration { com.android.internal.R.dimen.config_viewMaxFlingVelocity); mGlobalActionsKeyTimeout = res.getInteger( com.android.internal.R.integer.config_globalActionsKeyTimeout); + mScrollFactor = res.getDimensionPixelSize( + com.android.internal.R.dimen.config_scrollFactor); } /** @@ -669,6 +679,14 @@ public class ViewConfiguration { } /** + * @return Amount to scroll in response to a {@link MotionEvent#ACTION_SCROLL} event. Multiply + * this by the event's axis value to obtain the number of pixels to be scrolled. + */ + public int getScaledScrollFactor() { + return mScrollFactor; + } + + /** * The maximum drawing cache size expressed in bytes. * * @return the maximum size of View's drawing cache expressed in bytes diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 1b37ed47c392..0dbf00dd75aa 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -221,6 +221,7 @@ public interface WindowManager extends ViewManager { * @see #TYPE_BASE_APPLICATION * @see #TYPE_APPLICATION * @see #TYPE_APPLICATION_STARTING + * @see #TYPE_DRAWN_APPLICATION * @see #TYPE_APPLICATION_PANEL * @see #TYPE_APPLICATION_MEDIA * @see #TYPE_APPLICATION_SUB_PANEL @@ -244,6 +245,7 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_BASE_APPLICATION, to = "TYPE_BASE_APPLICATION"), @ViewDebug.IntToString(from = TYPE_APPLICATION, to = "TYPE_APPLICATION"), @ViewDebug.IntToString(from = TYPE_APPLICATION_STARTING, to = "TYPE_APPLICATION_STARTING"), + @ViewDebug.IntToString(from = TYPE_DRAWN_APPLICATION, to = "TYPE_DRAWN_APPLICATION"), @ViewDebug.IntToString(from = TYPE_APPLICATION_PANEL, to = "TYPE_APPLICATION_PANEL"), @ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA, to = "TYPE_APPLICATION_MEDIA"), @ViewDebug.IntToString(from = TYPE_APPLICATION_SUB_PANEL, to = "TYPE_APPLICATION_SUB_PANEL"), @@ -315,6 +317,13 @@ public interface WindowManager extends ViewManager { public static final int TYPE_APPLICATION_STARTING = 3; /** + * Window type: a variation on TYPE_APPLICATION that ensures the window + * manager will wait for this window to be drawn before the app is shown. + * In multiuser systems shows only on the owning user's window. + */ + public static final int TYPE_DRAWN_APPLICATION = 4; + + /** * End of types of application windows. */ public static final int LAST_APPLICATION_WINDOW = 99; @@ -1581,6 +1590,15 @@ public interface WindowManager extends ViewManager { public static final int ROTATION_ANIMATION_JUMPCUT = 2; /** + * Value for {@link #rotationAnimation} to specify seamless rotation mode. + * This works like JUMPCUT but will fall back to CROSSFADE if rotation + * can't be applied without pausing the screen. + * + * @hide + */ + public static final int ROTATION_ANIMATION_SEAMLESS = 3; + + /** * Define the exit and entry animations used on this window when the device is rotated. * This only has an affect if the incoming and outgoing topmost * opaque windows have the #FLAG_FULLSCREEN bit set and are not covered diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index a6d004fd0ec9..6dafe34fdc13 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -114,6 +114,7 @@ import android.view.ActionMode; import android.view.Choreographer; import android.view.ContextMenu; import android.view.DragEvent; +import android.view.GestureDetector; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.KeyCharacterMap; @@ -650,6 +651,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ private Editor mEditor; + private final GestureDetector mClickableSpanOnClickGestureDetector; + private static final int DEVICE_PROVISIONED_UNKNOWN = 0; private static final int DEVICE_PROVISIONED_NO = 1; private static final int DEVICE_PROVISIONED_YES = 2; @@ -1488,6 +1491,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) { setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); } + + mClickableSpanOnClickGestureDetector = new GestureDetector(context, + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + if (mLinksClickable && (mMovement != null) && + (mMovement instanceof LinkMovementMethod + || (mAutoLinkMask != 0 && isTextSelectable()))) { + ClickableSpan[] links = ((Spannable) mText).getSpans( + getSelectionStart(), getSelectionEnd(), ClickableSpan.class); + if (links.length > 0) { + links[0].onClick(TextView.this); + return true; + } + } + return false; + } + }); } private int[] parseDimensionArray(TypedArray dimens) { @@ -8515,21 +8536,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mMovement != null) { handled |= mMovement.onTouchEvent(this, (Spannable) mText, event); } + handled |= mClickableSpanOnClickGestureDetector.onTouchEvent(event); final boolean textIsSelectable = isTextSelectable(); - if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && textIsSelectable) { - // The LinkMovementMethod which should handle taps on links has not been installed - // on non editable text that support text selection. - // We reproduce its behavior here to open links for these. - ClickableSpan[] links = ((Spannable) mText).getSpans(getSelectionStart(), - getSelectionEnd(), ClickableSpan.class); - - if (links.length > 0) { - links[0].onClick(this); - handled = true; - } - } - if (touchIsFinished && (isTextEditable() || textIsSelectable)) { // Show the IME, except when selecting in read-only text. final InputMethodManager imm = InputMethodManager.peekInstance(); diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 1308f2818144..eb143e8bb9d0 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -101,6 +101,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATIO import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL; /** @hide */ @@ -1860,7 +1861,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } final WindowManager.LayoutParams attrs = mWindow.getAttributes(); final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION || - attrs.type == TYPE_APPLICATION; + attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION; // Only a non floating application window on one of the allowed workspaces can get a caption if (!mWindow.isFloating() && isApplication && StackId.hasWindowDecor(mStackId)) { // Dependent on the brightness of the used title we either use the diff --git a/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl b/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl index 419b1f8feac7..8e454db4cb04 100644 --- a/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl +++ b/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl @@ -20,4 +20,5 @@ interface IKeyguardStateCallback { void onSimSecureStateChanged(boolean simSecure); void onInputRestrictedStateChanged(boolean inputRestricted); void onTrustedChanged(boolean trusted); + void onHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper); }
\ No newline at end of file diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java index f137821faf44..b0d45e1d1db9 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -750,6 +750,14 @@ public class StateMachine { /** The destination state when transitionTo has been invoked */ private State mDestState; + /** + * Indicates if a transition is in progress + * + * This will be true for all calls of State.exit and all calls of State.enter except for the + * last enter call for the current destination state. + */ + private boolean mTransitionInProgress = false; + /** The list of deferred messages */ private ArrayList<Message> mDeferredMessages = new ArrayList<Message>(); @@ -862,6 +870,8 @@ public class StateMachine { * invoke the exit methods then the enter methods. */ StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState); + // flag is cleared in invokeEnterMethods before entering the target state + mTransitionInProgress = true; invokeExitMethods(commonStateInfo); int stateStackEnteringIndex = moveTempStateStackToStateStack(); invokeEnterMethods(stateStackEnteringIndex); @@ -1017,10 +1027,15 @@ public class StateMachine { */ private final void invokeEnterMethods(int stateStackEnteringIndex) { for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) { + if (stateStackEnteringIndex == mStateStackTopIndex) { + // Last enter state for transition + mTransitionInProgress = false; + } if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName()); mStateStack[i].state.enter(); mStateStack[i].active = true; } + mTransitionInProgress = false; // ensure flag set to false if no methods called } /** @@ -1196,6 +1211,10 @@ public class StateMachine { /** @see StateMachine#transitionTo(IState) */ private final void transitionTo(IState destState) { + if (mTransitionInProgress) { + Log.wtf(mSm.mName, "transitionTo called while transition already in progress to " + + mDestState + ", new target state=" + destState); + } mDestState = (State) destState; if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName()); } diff --git a/core/java/com/android/internal/widget/WatchHeaderListView.java b/core/java/com/android/internal/widget/WatchHeaderListView.java index 3d32d86cd49c..4fd19c37bf47 100644 --- a/core/java/com/android/internal/widget/WatchHeaderListView.java +++ b/core/java/com/android/internal/widget/WatchHeaderListView.java @@ -103,7 +103,8 @@ public class WatchHeaderListView extends ListView { @Override public int getHeaderViewsCount() { - return mTopPanel == null ? super.getHeaderViewsCount() : super.getHeaderViewsCount() + 1; + return mTopPanel == null ? super.getHeaderViewsCount() + : super.getHeaderViewsCount() + (mTopPanel.getVisibility() == GONE ? 0 : 1); } private void wrapAdapterIfNecessary() { @@ -133,7 +134,7 @@ public class WatchHeaderListView extends ListView { } private int getTopPanelCount() { - return mTopPanel == null ? 0 : 1; + return (mTopPanel == null || mTopPanel.getVisibility() == GONE) ? 0 : 1; } @Override @@ -143,33 +144,19 @@ public class WatchHeaderListView extends ListView { @Override public boolean areAllItemsEnabled() { - return mTopPanel == null && super.areAllItemsEnabled(); + return getTopPanelCount() == 0 && super.areAllItemsEnabled(); } @Override public boolean isEnabled(int position) { - if (mTopPanel != null) { - if (position == 0) { - return false; - } else { - return super.isEnabled(position - 1); - } - } - - return super.isEnabled(position); + int topPanelCount = getTopPanelCount(); + return position < topPanelCount ? false : super.isEnabled(position - topPanelCount); } @Override public Object getItem(int position) { - if (mTopPanel != null) { - if (position == 0) { - return null; - } else { - return super.getItem(position - 1); - } - } - - return super.getItem(position); + int topPanelCount = getTopPanelCount(); + return position < topPanelCount ? null : super.getItem(position - topPanelCount); } @Override @@ -187,15 +174,9 @@ public class WatchHeaderListView extends ListView { @Override public View getView(int position, View convertView, ViewGroup parent) { - if (mTopPanel != null) { - if (position == 0) { - return mTopPanel; - } else { - return super.getView(position - 1, convertView, parent); - } - } - - return super.getView(position, convertView, parent); + int topPanelCount = getTopPanelCount(); + return position < topPanelCount + ? mTopPanel : super.getView(position - topPanelCount, convertView, parent); } @Override diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 2ea80ff77c61..2a9498cf605b 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -79,6 +79,9 @@ LOCAL_SRC_FILES:= \ android_text_AndroidBidi.cpp \ android_text_StaticLayout.cpp \ android_os_Debug.cpp \ + android_os_HwBinder.cpp \ + android_os_HwParcel.cpp \ + android_os_HwRemoteBinder.cpp \ android_os_MemoryFile.cpp \ android_os_MessageQueue.cpp \ android_os_Parcel.cpp \ @@ -177,7 +180,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 +264,7 @@ LOCAL_SHARED_LIBRARIES := \ libradio_metadata \ libnativeloader \ libmemunreachable \ + libhwbinder \ LOCAL_SHARED_LIBRARIES += \ libhwui \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index be53cf343dd7..dc2748555c68 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -155,6 +155,9 @@ 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_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); @@ -1286,6 +1289,9 @@ 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_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_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index ef16ef5055de..497600212095 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -20,13 +20,14 @@ #define LOG_TAG "AudioSystem-JNI" #include <utils/Log.h> +#include <sstream> #include <jni.h> #include <JNIHelp.h> #include "core_jni_helpers.h" #include <media/AudioSystem.h> #include <media/AudioPolicy.h> - +#include <nativehelper/ScopedLocalRef.h> #include <system/audio.h> #include <system/audio_policy.h> #include "android_media_AudioFormat.h" @@ -903,6 +904,12 @@ static bool hasFormat(int* formats, size_t size, int format) { return false; // not found } +// TODO: pull out to separate file +template <typename T, size_t N> +static constexpr size_t array_size(const T (&)[N]) { + return N; +} + static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, const struct audio_port *nAudioPort) { @@ -923,6 +930,38 @@ static jint convertAudioPortFromNative(JNIEnv *env, ALOGV("convertAudioPortFromNative id %d role %d type %d name %s", nAudioPort->id, nAudioPort->role, nAudioPort->type, nAudioPort->name); + // Verify audio port array count info. + if (nAudioPort->num_sample_rates > array_size(nAudioPort->sample_rates) + || nAudioPort->num_channel_masks > array_size(nAudioPort->channel_masks) + || nAudioPort->num_formats > array_size(nAudioPort->formats) + || nAudioPort->num_gains > array_size(nAudioPort->gains)) { + + std::stringstream ss; + ss << "convertAudioPortFromNative array count out of bounds:" + << " num_sample_rates " << nAudioPort->num_sample_rates + << " num_channel_masks " << nAudioPort->num_channel_masks + << " num_formats " << nAudioPort->num_formats + << " num_gains " << nAudioPort->num_gains + ; + std::string s = ss.str(); + + // Prefer to log through Java wtf instead of native ALOGE. + ScopedLocalRef<jclass> jLogClass(env, env->FindClass("android/util/Log")); + jmethodID jWtfId = (jLogClass.get() == nullptr) + ? nullptr + : env->GetStaticMethodID(jLogClass.get(), "wtf", + "(Ljava/lang/String;Ljava/lang/String;)I"); + if (jWtfId != nullptr) { + ScopedLocalRef<jstring> jMessage(env, env->NewStringUTF(s.c_str())); + ScopedLocalRef<jstring> jTag(env, env->NewStringUTF(LOG_TAG)); + (void)env->CallStaticIntMethod(jLogClass.get(), jWtfId, jTag.get(), jMessage.get()); + } else { + ALOGE("%s", s.c_str()); + } + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + jSamplingRates = env->NewIntArray(nAudioPort->num_sample_rates); if (jSamplingRates == NULL) { jStatus = (jint)AUDIO_JAVA_ERROR; @@ -1066,7 +1105,7 @@ static jint convertAudioPortFromNative(JNIEnv *env, &jAudioPortConfig, &nAudioPort->active_config); if (jStatus != AUDIO_JAVA_SUCCESS) { - return jStatus; + goto exit; } env->SetObjectField(*jAudioPort, gAudioPortFields.mActiveConfig, jAudioPortConfig); diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp new file mode 100644 index 000000000000..a45e1eef2e8f --- /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 <hwbinder/IServiceManager.h> +#include <hwbinder/ProcessState.h> +#include <hwbinder/Status.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_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp new file mode 100644 index 000000000000..0202303b10ea --- /dev/null +++ b/core/jni/android_os_HwParcel.cpp @@ -0,0 +1,913 @@ +/* + * 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_HwRemoteBinder.h" + +#include <JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> +#include <hwbinder/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 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)); + + return env->NewObject( + clazz.get(), gFields.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(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 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_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 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); + + string_vec *vec = + (string_vec *)impl->getStorage()->allocTemporaryStorage( + sizeof(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 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 }, + + { "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 }, + + { "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 }, + + { "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 }, + + { "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 }, +}; + +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..23d4fced2183 --- /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 <hwbinder/IServiceManager.h> +#include <hwbinder/Status.h> +#include <nativehelper/ScopedLocalRef.h> + +#include "core_jni_helpers.h" + +using android::AndroidRuntime; + +#define PACKAGE_PATH "android/os" +#define CLASS_NAME "HwRemoteBinder" +#define CLASS_PATH PACKAGE_PATH "/" CLASS_NAME + +namespace android { + +static struct fields_t { + jfieldID contextID; + jmethodID constructID; + +} gFields; + +// static +void JHwRemoteBinder::InitClass(JNIEnv *env) { + ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH)); + + gFields.contextID = + GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J"); + + gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V"); +} + +// static +sp<JHwRemoteBinder> JHwRemoteBinder::SetNativeContext( + JNIEnv *env, jobject thiz, const sp<JHwRemoteBinder> &context) { + sp<JHwRemoteBinder> old = + (JHwRemoteBinder *)env->GetLongField(thiz, gFields.contextID); + + if (context != NULL) { + context->incStrong(NULL /* id */); + } + + if (old != NULL) { + old->decStrong(NULL /* id */); + } + + env->SetLongField(thiz, gFields.contextID, (long)context.get()); + + return old; +} + +// static +sp<JHwRemoteBinder> JHwRemoteBinder::GetNativeContext( + JNIEnv *env, jobject thiz) { + return (JHwRemoteBinder *)env->GetLongField(thiz, gFields.contextID); +} + +// static +jobject JHwRemoteBinder::NewObject( + JNIEnv *env, const sp<hardware::IBinder> &binder) { + ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH)); + + // XXX Have to look up the constructor here because otherwise that static + // class initializer isn't called and gFields.constructID is undefined :( + + jmethodID constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V"); + + jobject obj = env->NewObject(clazz.get(), constructID); + JHwRemoteBinder::GetNativeContext(env, obj)->setBinder(binder); + + return obj; +} + +JHwRemoteBinder::JHwRemoteBinder( + JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder) + : mBinder(binder) { + jclass clazz = env->GetObjectClass(thiz); + CHECK(clazz != NULL); + + mClass = (jclass)env->NewGlobalRef(clazz); + mObject = env->NewWeakGlobalRef(thiz); +} + +JHwRemoteBinder::~JHwRemoteBinder() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + + env->DeleteGlobalRef(mClass); + mClass = NULL; +} + +sp<hardware::IBinder> JHwRemoteBinder::getBinder() { + return mBinder; +} + +void JHwRemoteBinder::setBinder(const sp<hardware::IBinder> &binder) { + mBinder = binder; +} + +} // namespace android + +//////////////////////////////////////////////////////////////////////////////// + +using namespace android; + +static void releaseNativeContext(void *nativeContext) { + sp<JHwRemoteBinder> binder = (JHwRemoteBinder *)nativeContext; + + if (binder != NULL) { + binder->decStrong(NULL /* id */); + } +} + +static jlong JHwRemoteBinder_native_init(JNIEnv *env) { + JHwRemoteBinder::InitClass(env); + + return reinterpret_cast<jlong>(&releaseNativeContext); +} + +static void JHwRemoteBinder_native_setup_empty(JNIEnv *env, jobject thiz) { + sp<JHwRemoteBinder> context = + new JHwRemoteBinder(env, thiz, NULL /* service */); + + JHwRemoteBinder::SetNativeContext(env, thiz, context); +} + +static void JHwRemoteBinder_native_transact( + JNIEnv *env, + jobject thiz, + jint code, + jobject requestObj, + jobject replyObj, + jint flags) { + sp<hardware::IBinder> binder = + JHwRemoteBinder::GetNativeContext(env, thiz)->getBinder(); + + if (requestObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + const hardware::Parcel *request = + JHwParcel::GetNativeContext(env, requestObj)->getParcel(); + + hardware::Parcel *reply = + JHwParcel::GetNativeContext(env, replyObj)->getParcel(); + + status_t err = binder->transact(code, *request, reply, flags); + signalExceptionForError(env, err); +} + +static JNINativeMethod gMethods[] = { + { "native_init", "()J", (void *)JHwRemoteBinder_native_init }, + + { "native_setup_empty", "()V", + (void *)JHwRemoteBinder_native_setup_empty }, + + { "transact", + "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V", + (void *)JHwRemoteBinder_native_transact }, +}; + +namespace android { + +int register_android_os_HwRemoteBinder(JNIEnv *env) { + return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods)); +} + +} // namespace android + diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h new file mode 100644 index 000000000000..fd33338986a0 --- /dev/null +++ b/core/jni/android_os_HwRemoteBinder.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_OS_HW_REMOTE_BINDER_H +#define ANDROID_OS_HW_REMOTE_BINDER_H + +#include <android-base/macros.h> +#include <hwbinder/Binder.h> +#include <jni.h> +#include <utils/RefBase.h> + +namespace android { + +struct JHwRemoteBinder : public RefBase { + static void InitClass(JNIEnv *env); + + static sp<JHwRemoteBinder> SetNativeContext( + JNIEnv *env, jobject thiz, const sp<JHwRemoteBinder> &context); + + static sp<JHwRemoteBinder> GetNativeContext(JNIEnv *env, jobject thiz); + + static jobject NewObject(JNIEnv *env, const sp<hardware::IBinder> &binder); + + JHwRemoteBinder( + JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder); + + sp<hardware::IBinder> getBinder(); + void setBinder(const sp<hardware::IBinder> &binder); + +protected: + virtual ~JHwRemoteBinder(); + +private: + jclass mClass; + jobject mObject; + + sp<hardware::IBinder> mBinder; + + DISALLOW_COPY_AND_ASSIGN(JHwRemoteBinder); +}; + +int register_android_os_HwRemoteBinder(JNIEnv *env); + +} // namespace android + +#endif // ANDROID_OS_HW_REMOTE_BINDER_H + diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index ffa710752956..1cfbd97f82b9 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -22,7 +22,6 @@ #include <utils/Log.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> -#include <cutils/process_name.h> #include <cutils/sched_policy.h> #include <utils/String8.h> #include <utils/Vector.h> diff --git a/core/jni/hwbinder/EphemeralStorage.cpp b/core/jni/hwbinder/EphemeralStorage.cpp new file mode 100644 index 000000000000..e5087081ba55 --- /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); \ + \ + hidl_vec<Type> *vec = \ + (hidl_vec<Type> *)allocTemporaryStorage(sizeof(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..1273003e6176 --- /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 <hwbinder/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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 3ceba08079be..effc3fc8b1bd 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -181,8 +181,13 @@ <protected-broadcast android:name="android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast + android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT" /> + <protected-broadcast + android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" /> + <protected-broadcast android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast android:name="android.btopp.intent.action.INCOMING_FILE_NOTIFICATION" /> <protected-broadcast android:name="android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT" /> <protected-broadcast android:name="android.btopp.intent.action.LIST" /> @@ -199,6 +204,8 @@ <protected-broadcast android:name="com.android.bluetooth.pbap.userconfirmtimeout" /> <protected-broadcast android:name="com.android.bluetooth.pbap.authresponse" /> <protected-broadcast android:name="com.android.bluetooth.pbap.authcancelled" /> + <protected-broadcast android:name="com.android.bluetooth.sap.USER_CONFIRM_TIMEOUT" /> + <protected-broadcast android:name="com.android.bluetooth.sap.action.DISCONNECT_ACTION" /> <protected-broadcast android:name="android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED" /> @@ -395,6 +402,8 @@ <protected-broadcast android:name="android.bluetooth.adapter.action.BLE_STATE_CHANGED" /> <protected-broadcast android:name="com.android.bluetooth.map.USER_CONFIRM_TIMEOUT" /> + <protected-broadcast android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT" /> + <protected-broadcast android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" /> <protected-broadcast android:name="android.content.jobscheduler.JOB_DELAY_EXPIRED" /> <protected-broadcast android:name="android.content.syncmanager.SYNC_ALARM" /> <protected-broadcast android:name="android.media.INTERNAL_RINGER_MODE_CHANGED_ACTION" /> diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml index 34d42d0b769c..b37409503caf 100644 --- a/core/res/res/values-gl-rES/strings.xml +++ b/core/res/res/values-gl-rES/strings.xml @@ -598,7 +598,7 @@ <string name="phoneTypeCallback" msgid="2712175203065678206">"Devolver chamada"</string> <string name="phoneTypeCar" msgid="8738360689616716982">"Coche"</string> <string name="phoneTypeCompanyMain" msgid="540434356461478916">"Empresa (ppal.)"</string> - <string name="phoneTypeIsdn" msgid="8022453193171370337">"ISDN"</string> + <string name="phoneTypeIsdn" msgid="8022453193171370337">"RDSI"</string> <string name="phoneTypeMain" msgid="6766137010628326916">"Principal"</string> <string name="phoneTypeOtherFax" msgid="8587657145072446565">"Outro fax"</string> <string name="phoneTypeRadio" msgid="4093738079908667513">"Radio"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 324275200ec2..101c6ecc5717 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1362,7 +1362,7 @@ <string name="action_menu_overflow_description" msgid="2295659037509008453">"אפשרויות נוספות"</string> <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string> <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string> - <string name="storage_internal" msgid="3570990907910199483">"אחסון משותף פנימי"</string> + <string name="storage_internal" msgid="3570990907910199483">"אחסון שיתוף פנימי"</string> <string name="storage_sd_card" msgid="3282948861378286745">"כרטיס SD"</string> <string name="storage_sd_card_label" msgid="6347111320774379257">"כרטיס SD של <xliff:g id="MANUFACTURER">%s</xliff:g>"</string> <string name="storage_usb_drive" msgid="6261899683292244209">"כונן USB"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index fe3ebe4e23d8..8810bf9754b8 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1310,7 +1310,7 @@ <string name="action_menu_overflow_description" msgid="2295659037509008453">"옵션 더보기"</string> <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string> <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string> - <string name="storage_internal" msgid="3570990907910199483">"내부 공유 저장공간"</string> + <string name="storage_internal" msgid="3570990907910199483">"내부 공유 저장용량"</string> <string name="storage_sd_card" msgid="3282948861378286745">"SD 카드"</string> <string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD 카드"</string> <string name="storage_usb_drive" msgid="6261899683292244209">"USB 드라이브"</string> diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml index bccb5fa2cd95..53ada581d073 100644 --- a/core/res/res/values-ky-rKG/strings.xml +++ b/core/res/res/values-ky-rKG/strings.xml @@ -1197,8 +1197,7 @@ <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Колдонмого орнотуу сеанстарын окуу мүмкүнчүлүгүн берет. Ушуну менен, ал жигердүү топтом орнотууларынын чоо-жайын көрө алат."</string> <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"орнотуу топтомдорун суроо"</string> <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Колдонмо топтомдорду орнотууга уруксат сурай алат."</string> - <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) --> - <skip /> + <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Масштабдын параметрлерин өзгөртүү үчүн бул жерди эки жолу басыңыз."</string> <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Виджетти кошуу мүмкүн болбоду."</string> <string name="ime_action_go" msgid="8320845651737369027">"Өтүү"</string> <string name="ime_action_search" msgid="658110271822807811">"Издөө"</string> @@ -1229,10 +1228,8 @@ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Эскертмелердин маанилүүлүгүн баалоо кызматы"</string> <string name="vpn_title" msgid="19615213552042827">"VPN иштетилди"</string> <string name="vpn_title_long" msgid="6400714798049252294">"VPN <xliff:g id="APP">%s</xliff:g> аркылуу жандырылды"</string> - <!-- no translation found for vpn_text (1610714069627824309) --> - <skip /> - <!-- no translation found for vpn_text_long (4907843483284977618) --> - <skip /> + <string name="vpn_text" msgid="1610714069627824309">"Тармактын параметрлерин өзгөртүү үчүн бул жерди басыңыз."</string> + <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> сеансына туташуу ишке ашты. Желенин параметрлерин өзгөртүү үчүн бул жерди басыңыз."</string> <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Дайым иштеген VPN туташууда…"</string> <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Дайым иштеген VPN туташтырылды"</string> <string name="vpn_lockdown_error" msgid="6009249814034708175">"Дайым иштеген VPN\'де ката кетти"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 4c6f29fd75c9..21daba90c7ec 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -1310,7 +1310,7 @@ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string> <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string> <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string> - <string name="storage_internal" msgid="3570990907910199483">"Armazenamento interno partilhado"</string> + <string name="storage_internal" msgid="3570990907910199483">"Armazen. interno partilhado"</string> <string name="storage_sd_card" msgid="3282948861378286745">"Cartão SD"</string> <string name="storage_sd_card_label" msgid="6347111320774379257">"Cartão SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string> <string name="storage_usb_drive" msgid="6261899683292244209">"Unidade USB"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 6161494353f2..6e2e9f6e528d 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1764,6 +1764,10 @@ <!-- Amount of time in ms the user needs to press the relevant key to bring up the global actions dialog --> <integer name="config_globalActionsKeyTimeout">500</integer> + <!-- Distance that should be scrolled in response to a {@link MotionEvent#ACTION_SCROLL event} + with an axis value of 1. --> + <dimen name="config_scrollFactor">64dp</dimen> + <!-- Maximum number of grid columns permitted in the ResolverActivity used for picking activities to handle an intent. --> <integer name="config_maxResolverActivityColumns">3</integer> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 3c2b55b033b9..9d806b9cf798 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -418,6 +418,7 @@ <java-symbol type="dimen" name="config_viewConfigurationTouchSlop" /> <java-symbol type="dimen" name="config_viewMinFlingVelocity" /> <java-symbol type="dimen" name="config_viewMaxFlingVelocity" /> + <java-symbol type="dimen" name="config_scrollFactor" /> <java-symbol type="dimen" name="default_app_widget_padding_bottom" /> <java-symbol type="dimen" name="default_app_widget_padding_left" /> <java-symbol type="dimen" name="default_app_widget_padding_right" /> diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java index 9074f8a97132..d48a67a4c72f 100644 --- a/core/tests/coretests/src/android/net/NetworkStatsTest.java +++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java @@ -454,7 +454,7 @@ public class NetworkStatsTest extends TestCase { .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L); assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface)); - assertEquals(21, delta.size()); + assertEquals(20, delta.size()); // tunIface and TEST_IFACE entries are not changed. assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO, @@ -478,38 +478,89 @@ public class NetworkStatsTest extends TestCase { // Existing underlying Iface entries are updated assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO, - 44783L, 54L, 13829L, 60L, 0L); + 44783L, 54L, 14178L, 62L, 0L); assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 0L, 0L, 0L, 0L, 0L); // VPN underlying Iface entries are updated assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_NO, - 28304L, 27L, 1719L, 12L, 0L); + 28304L, 27L, 1L, 2L, 0L); assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 0L, 0L, 0L, 0L, 0L); // New entries are added for new application's underlying Iface traffic assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE, ROAMING_NO, - 72667L, 197L, 41872L, 219L, 0L); + 72667L, 197L, 43123L, 227L, 0L); assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE, ROAMING_NO, - 9297L, 17L, 3936, 19L, 0L); + 9297L, 17L, 4054, 19L, 0L); assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1, ROAMING_NO, - 21691L, 41L, 13179L, 46L, 0L); + 21691L, 41L, 13572L, 48L, 0L); assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1, ROAMING_NO, - 1281L, 2L, 634L, 1L, 0L); + 1281L, 2L, 653L, 1L, 0L); // New entries are added for debug purpose assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO, - 39605L, 46L, 11690, 49, 0); + 39605L, 46L, 12039, 51, 0); assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO, - 81964, 214, 45808, 238, 0); - assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO, - 4983, 10, 1717, 10, 0); + 81964, 214, 47177, 246, 0); assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, ROAMING_ALL, - 126552, 270, 59215, 297, 0); + 121569, 260, 59216, 297, 0); } + // Tests a case where all of the data received by the tun0 interface is echo back into the tun0 + // interface by the vpn app before it's sent out of the underlying interface. The VPN app should + // not be charged for the echoed data but it should still be charged for any extra data it sends + // via the underlying interface. + public void testMigrateTun_VpnAsLoopback() { + final int tunUid = 10030; + final String tunIface = "tun0"; + final String underlyingIface = "wlan0"; + NetworkStats delta = new NetworkStats(TEST_START, 9) + // 2 different apps sent/receive data via tun0. + .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, 50000L, 25L, 100000L, 50L, 0L) + .addValues(tunIface, 20100, SET_DEFAULT, TAG_NONE, 500L, 2L, 200L, 5L, 0L) + // VPN package resends data through the tunnel (with exaggerated overhead) + .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, 240000, 100L, 120000L, 60L, 0L) + // 1 app already has some traffic on the underlying interface, the other doesn't yet + .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, 1000L, 10L, 2000L, 20L, 0L) + // Traffic through the underlying interface via the vpn app. + // This test should redistribute this data correctly. + .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, + 75500L, 37L, 130000L, 70L, 0L); + + assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface)); + assertEquals(9, delta.size()); + + // tunIface entries should not be changed. + assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO, + 50000L, 25L, 100000L, 50L, 0L); + assertValues(delta, 1, tunIface, 20100, SET_DEFAULT, TAG_NONE, ROAMING_NO, + 500L, 2L, 200L, 5L, 0L); + assertValues(delta, 2, tunIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_NO, + 240000L, 100L, 120000L, 60L, 0L); + + // Existing underlying Iface entries are updated + assertValues(delta, 3, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO, + 51000L, 35L, 102000L, 70L, 0L); + + // VPN underlying Iface entries are updated + assertValues(delta, 4, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_NO, + 25000L, 10L, 29800L, 15L, 0L); + + // New entries are added for new application's underlying Iface traffic + assertContains(delta, underlyingIface, 20100, SET_DEFAULT, TAG_NONE, ROAMING_NO, + 500L, 2L, 200L, 5L, 0L); + + // New entries are added for debug purpose + assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO, + 50000L, 25L, 100000L, 50L, 0L); + assertContains(delta, underlyingIface, 20100, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO, + 500, 2L, 200L, 5L, 0L); + assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, ROAMING_ALL, + 50500L, 27L, 100200L, 55, 0); + } + private static void assertContains(NetworkStats stats, String iface, int uid, int set, int tag, int roaming, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java index 522149941416..3ccbf17c815e 100644 --- a/core/tests/coretests/src/android/widget/TextViewTest.java +++ b/core/tests/coretests/src/android/widget/TextViewTest.java @@ -29,6 +29,7 @@ import android.text.Spannable; * TextViewTest tests {@link TextView}. */ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewActivity> { + private TextView mTextView; public TextViewTest() { super(TextViewActivity.class); @@ -37,16 +38,22 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewActiv @SmallTest @Presubmit public void testArray() throws Exception { - TextView tv = new TextView(getActivity()); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + mTextView = new TextView(getActivity()); + } + }); + getInstrumentation().waitForIdleSync(); char[] c = new char[] { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; - tv.setText(c, 1, 4); - CharSequence oldText = tv.getText(); + mTextView.setText(c, 1, 4); + CharSequence oldText = mTextView.getText(); - tv.setText(c, 4, 5); - CharSequence newText = tv.getText(); + mTextView.setText(c, 4, 5); + CharSequence newText = mTextView.getText(); assertTrue(newText == oldText); @@ -67,12 +74,18 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewActiv @SmallTest public void testProcessTextActivityResultNonEditable() { - final TextView tv = new TextView(getActivity()); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + mTextView = new TextView(getActivity()); + } + }); + getInstrumentation().waitForIdleSync(); CharSequence originalText = "This is some text."; - tv.setText(originalText, TextView.BufferType.SPANNABLE); - assertEquals(originalText, tv.getText().toString()); - tv.setTextIsSelectable(true); - Selection.setSelection((Spannable) tv.getText(), 0, tv.getText().length()); + mTextView.setText(originalText, TextView.BufferType.SPANNABLE); + assertEquals(originalText, mTextView.getText().toString()); + mTextView.setTextIsSelectable(true); + Selection.setSelection((Spannable) mTextView.getText(), 0, mTextView.getText().length()); // We need to run this in the UI thread, as it will create a Toast. getActivity().runOnUiThread(new Runnable() { @@ -81,60 +94,79 @@ public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewActiv CharSequence newText = "Text is replaced."; Intent data = new Intent(); data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText); - tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, data); + mTextView.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, data); } }); getInstrumentation().waitForIdleSync(); // This is a TextView, which can't be modified. Hence no change should have been made. - assertEquals(originalText, tv.getText().toString()); + assertEquals(originalText, mTextView.getText().toString()); } @SmallTest public void testProcessTextActivityResultEditable() { - EditText tv = new EditText(getActivity()); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + mTextView = new EditText(getActivity()); + } + }); + getInstrumentation().waitForIdleSync(); CharSequence originalText = "This is some text."; - tv.setText(originalText, TextView.BufferType.SPANNABLE); - assertEquals(originalText, tv.getText().toString()); - tv.setTextIsSelectable(true); - Selection.setSelection(tv.getText(), 0, tv.getText().length()); + mTextView.setText(originalText, TextView.BufferType.SPANNABLE); + assertEquals(originalText, mTextView.getText().toString()); + mTextView.setTextIsSelectable(true); + Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length()); CharSequence newText = "Text is replaced."; Intent data = new Intent(); data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText); - tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, data); + mTextView.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, data); - assertEquals(newText, tv.getText().toString()); + assertEquals(newText, mTextView.getText().toString()); } @SmallTest public void testProcessTextActivityResultCancel() { - EditText tv = new EditText(getActivity()); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + mTextView = new EditText(getActivity()); + } + }); + getInstrumentation().waitForIdleSync(); CharSequence originalText = "This is some text."; - tv.setText(originalText, TextView.BufferType.SPANNABLE); - assertEquals(originalText, tv.getText().toString()); - tv.setTextIsSelectable(true); - Selection.setSelection(tv.getText(), 0, tv.getText().length()); + mTextView.setText(originalText, TextView.BufferType.SPANNABLE); + assertEquals(originalText, mTextView.getText().toString()); + mTextView.setTextIsSelectable(true); + Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length()); CharSequence newText = "Text is replaced."; Intent data = new Intent(); data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText); - tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_CANCELED, data); + mTextView.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_CANCELED, + data); - assertEquals(originalText, tv.getText().toString()); + assertEquals(originalText, mTextView.getText().toString()); } @SmallTest public void testProcessTextActivityNoData() { - EditText tv = new EditText(getActivity()); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + mTextView = new EditText(getActivity()); + } + }); + getInstrumentation().waitForIdleSync(); CharSequence originalText = "This is some text."; - tv.setText(originalText, TextView.BufferType.SPANNABLE); - assertEquals(originalText, tv.getText().toString()); - tv.setTextIsSelectable(true); - Selection.setSelection(tv.getText(), 0, tv.getText().length()); + mTextView.setText(originalText, TextView.BufferType.SPANNABLE); + assertEquals(originalText, mTextView.getText().toString()); + mTextView.setTextIsSelectable(true); + Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length()); - tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, null); + mTextView.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, null); - assertEquals(originalText, tv.getText().toString()); + assertEquals(originalText, mTextView.getText().toString()); } } diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml index fd54b7d8aa3d..a1581144e56c 100644 --- a/docs/html/_redirects.yaml +++ b/docs/html/_redirects.yaml @@ -153,6 +153,8 @@ redirects: to: /google/play/billing/index.html - from: /guide/developing/tools/proguard.html to: /studio/build/shrink-code.html +- from: /guide/developing/tools/aidl.html + to: /guide/components/aidl.html - from: /guide/developing/tools/... to: /studio/command-line/ - from: /guide/developing/... diff --git a/docs/html/guide/components/intents-common.jd b/docs/html/guide/components/intents-common.jd index 9488f6ecb982..e6c9fc691cbb 100644 --- a/docs/html/guide/components/intents-common.jd +++ b/docs/html/guide/components/intents-common.jd @@ -99,7 +99,6 @@ page.tags="IntentFilter" </ol> </li> <li><a href="#AdbIntents">Verify Intents with the Android Debug Bridge</a></li> - <li><a href="#Now">Intents Fired by Google Now</a></li> </ol> <h2>See also</h2> @@ -110,9 +109,9 @@ Filters</a></li> </div> </div> -<!-- Google Now box styles --> +<!-- Google Voice Actions box styles --> <style type="text/css"> -.now-box { +.voice-box { border-color: rgb(204,204,204); border-style: solid; border-width: 1px; @@ -121,99 +120,120 @@ Filters</a></li> padding: 17px; width: 200px; } -.now-box li { +.voice-box li { font-size: 13px; font-style: italic; margin-top: 0px; } -.now-box ul { +.voice-box ul { margin-bottom: 0px; } -.now-img { +.voice-img { width: 30px; margin-bottom: 0px !important; } -.now-img-cont { +.voice-img-cont { float: left; margin-right: 10px; } -.now-title { +.voice-title { font-weight: bold; margin-top: 7px; } -.now-list { +.voice-list { font-size: 13px; margin-bottom: 10px !important; list-style-type: none; } -.now-list li { +.voice-list li { font-style: italic; } </style> -<p>An intent allows you to start an activity in another app by describing a simple -action you'd like to perform (such as "view a map" or "take a picture") -in an {@link android.content.Intent} object. This type of intent is -called an <em>implicit</em> intent because it does not specify the app component -to start, but instead specifies an <em>action</em> and provides some -<em>data</em> with which to perform the action.</p> - -<p>When you call -{@link android.content.Context#startActivity startActivity()} or -{@link android.app.Activity#startActivityForResult startActivityForResult()} and pass it an -implicit intent, the system <a href="{@docRoot}guide/components/intents-filters.html#Resolution" ->resolves the intent</a> to an app that can handle the intent -and starts its corresponding {@link android.app.Activity}. If there's more than one app -that can handle the intent, the system presents the user with a dialog to pick which app -to use.</p> - -<p>This page describes several implicit intents that you can use to perform common actions, -organized by the type of app that handles the intent. Each section also shows how you can -create an <a href="{@docRoot}guide/components/intents-filters.html#Receiving">intent filter</a> to -advertise your app's ability to perform the same action.</p> - -<p class="caution"><strong>Caution:</strong> If there are no apps on the device that can receive -the implicit intent, your app will crash when it calls {@link android.content.Context#startActivity -startActivity()}. To first verify that an app exists to receive the intent, call {@link -android.content.Intent#resolveActivity resolveActivity()} on your {@link android.content.Intent} -object. If the result is non-null, there is at least one app that can handle the intent and -it's safe to call {@link android.content.Context#startActivity startActivity()}. If the result is -null, you should not use the intent and, if possible, you should disable the feature that invokes -the intent.</p> - -<p>If you're not familiar with how to create intents or intent filters, you should first read -<a href="{@docRoot}guide/components/intents-filters.html">Intents and Intent Filters</a>.</p> - -<p>To learn how to fire the intents listed on this page from your development host, see -<a href="#AdbIntents">Verify Intents with the Android Debug Bridge</a>.</p> +<p> + An intent allows you to start an activity in another app by describing a + simple action you'd like to perform (such as "view a map" or "take a + picture") in an {@link android.content.Intent} object. This type of intent + is called an <em>implicit</em> intent because it does not specify the app + component to start, but instead specifies an <em>action</em> and provides + some <em>data</em> with which to perform the action. +</p> -<h4>Google Now</h4> +<p> + When you call {@link android.content.Context#startActivity startActivity()} + or {@link android.app.Activity#startActivityForResult + startActivityForResult()} and pass it an implicit intent, the system + <a href="{@docRoot}guide/components/intents-filters.html#Resolution">resolves + the intent</a> to an app that can handle the intent and starts its + corresponding {@link android.app.Activity}. If there's more than one app + that can handle the intent, the system presents the user with a dialog to + pick which app to use. +</p> -<p><a href="http://www.google.com/landing/now/">Google Now</a> fires some of the intents listed -on this page in response to voice commands. For more information, see -<a href="#Now">Intents Fired by Google Now</a>.</p> +<p> + This page describes several implicit intents that you can use to perform + common actions, organized by the type of app that handles the intent. Each + section also shows how you can create an <a href= + "{@docRoot}guide/components/intents-filters.html#Receiving">intent + filter</a> to advertise your app's ability to perform the same action. +</p> +<p class="caution"> + <strong>Caution:</strong> If there are no apps on the device that can + receive the implicit intent, your app will crash when it calls {@link + android.content.Context#startActivity startActivity()}. To first verify that + an app exists to receive the intent, call {@link + android.content.Intent#resolveActivity resolveActivity()} on your {@link + android.content.Intent} object. If the result is non-null, there is at least + one app that can handle the intent and it's safe to call {@link + android.content.Context#startActivity startActivity()}. If the result is + null, you should not use the intent and, if possible, you should disable the + feature that invokes the intent. +</p> +<p> + If you're not familiar with how to create intents or intent filters, you + should first read <a href= + "{@docRoot}guide/components/intents-filters.html">Intents and Intent + Filters</a>. +</p> +<p> + To learn how to fire the intents listed on this page from your development + host, see <a href="#AdbIntents">Verify Intents with the Android Debug + Bridge</a>. +</p> +<h4>Google Voice Actions</h4> +<p> + <a href="https://developers.google.com/voice-actions/">Google Voice + Actions</a> fires some of the intents listed on this page in response to + voice commands. For more information, see <a href= + "https://developers.google.com/voice-actions/system/#system_actions_reference"> + Intents fired by Google Voice Actions</a>. +</p> <h2 id="Clock">Alarm Clock</h2> - <h3 id="CreateAlarm">Create an alarm</h3> -<!-- Google Now box --> -<div class="now-box"> - <div class="now-img-cont"> - <a href="#Now"> - <img src="{@docRoot}guide/components/images/voice-icon.png" class="now-img" width="30" - height="30" alt=""/> - </a> +<!-- Google Voice Actions box --> +<div class="voice-box"> + <div class="voice-img-cont"> + <a href= + "https://developers.google.com/voice-actions/system/#system_actions_reference"> + <img src="{@docRoot}guide/components/images/voice-icon.png" class= + "voice-img" width="30" height="30" alt=""></a> </div> - <p class="now-title">Google Now</p> + + <p class="voice-title"> + Google Voice Actions + </p> + <ul> - <li>"set an alarm for 7 am"</li> + <li>"set an alarm for 7 am" + </li> </ul> </div> @@ -301,22 +321,30 @@ android.provider.AlarmClock#ACTION_SET_ALARM} intent, your app must have the <h3 id="CreateTimer">Create a timer</h3> -<!-- Google Now box --> -<div class="now-box"> - <div class="now-img-cont"> - <a href="#Now"> - <img src="{@docRoot}guide/components/images/voice-icon.png" class="now-img" - width="30" height="30" alt=""/> - </a> +<!-- Google Voice Actions box --> +<div class="voice-box"> + <div class="voice-img-cont"> + <a href= + "https://developers.google.com/voice-actions/system/#system_actions_reference"> + <img src="{@docRoot}guide/components/images/voice-icon.png" class= + "voice-img" width="30" height="30" alt=""></a> </div> - <p class="now-title">Google Now</p> + + <p class="voice-title"> + Google Voice Actions + </p> + <ul> - <li>"set timer for 5 minutes"</li> + <li>"set timer for 5 minutes" + </li> </ul> </div> -<p>To create a countdown timer, use the {@link android.provider.AlarmClock#ACTION_SET_TIMER} -action and specify timer details such as the duration using extras defined below.</p> +<p> + To create a countdown timer, use the {@link + android.provider.AlarmClock#ACTION_SET_TIMER} action and specify timer + details such as the duration using extras defined below. +</p> <p class="note"><strong>Note:</strong> This intent was added in Android 4.4 (API level 19).</p> @@ -594,28 +622,36 @@ how to create an appropriate {@link android.net.Uri} for the output location, re </activity> </pre> -<p>When handling this intent, your activity should check for the {@link -android.provider.MediaStore#EXTRA_OUTPUT} extra in the incoming {@link android.content.Intent}, -then save the captured image or video at the location specified by that extra and call {@link -android.app.Activity#setResult(int,Intent) setResult()} with an -{@link android.content.Intent} that includes a compressed thumbnail -in an extra named <code>"data"</code>.</p> +<p> + When handling this intent, your activity should check for the {@link + android.provider.MediaStore#EXTRA_OUTPUT} extra in the incoming {@link + android.content.Intent}, then save the captured image or video at the + location specified by that extra and call {@link + android.app.Activity#setResult(int,Intent) setResult()} with an {@link + android.content.Intent} that includes a compressed thumbnail in an extra + named <code>"data"</code>. +</p> <h3 id="CameraStill">Start a camera app in still image mode</h3> -<!-- Google Now box --> -<div class="now-box"> - <div class="now-img-cont"> - <a href="#Now"> - <img src="{@docRoot}guide/components/images/voice-icon.png" class="now-img" - width="30" height="30" alt=""/> - </a> +<!-- Google Voice Actions box --> +<div class="voice-box"> + <div class="voice-img-cont"> + <a href= + "https://developers.google.com/voice-actions/system/#system_actions_reference"> + <img src="{@docRoot}guide/components/images/voice-icon.png" class= + "voice-img" width="30" height="30" alt=""></a> </div> - <p class="now-title">Google Now</p> + + <p class="voice-title"> + Google Voice Actions + </p> + <ul> - <li>"take a picture"</li> + <li>"take a picture" + </li> </ul> </div> @@ -661,17 +697,22 @@ public void capturePhoto() { <h3 id="CameraVideo">Start a camera app in video mode</h3> -<!-- Google Now box --> -<div class="now-box"> - <div class="now-img-cont"> - <a href="#Now"> - <img src="{@docRoot}guide/components/images/voice-icon.png" class="now-img" - width="30" height="30" alt=""/> - </a> +<!-- Google Voice Actions box --> +<div class="voice-box"> + <div class="voice-img-cont"> + <a href= + "https://developers.google.com/voice-actions/system/#system_actions_reference"> + <img src="{@docRoot}guide/components/images/voice-icon.png" class= + "voice-img" width="30" height="30" alt=""></a> </div> - <p class="now-title">Google Now</p> + + <p class="voice-title"> + Google Voice Actions + </p> + <ul> - <li>"record a video"</li> + <li>"record a video" + </li> </ul> </div> @@ -1348,20 +1389,30 @@ Framework</a> guide.</p> <h3 id="CallCar">Call a car</h3> -<!-- Google Now box --> -<div class="now-box"> - <div class="now-img-cont"> - <a href="#Now"> - <img src="{@docRoot}guide/components/images/voice-icon.png" class="now-img" - width="30" height="30" alt=""/> - </a> +<!-- Google Voice Actions box --> +<div class="voice-box"> + <div class="voice-img-cont"> + <a href= + "https://developers.google.com/voice-actions/system/#system_actions_reference"> + <img src="{@docRoot}guide/components/images/voice-icon.png" class= + "voice-img" width="30" height="30" alt=""></a> </div> - <p class="now-title">Google Now</p> + + <p class="voice-title"> + Google Voice Actions + </p> + <ul> - <li>"get me a taxi"</li> - <li>"call me a car"</li> + <li>"get me a taxi" + </li> + + <li>"call me a car" + </li> </ul> - <p style="font-size:13px;margin-bottom:0px;margin-top:10px">(Android Wear only)</p> + + <p style="font-size:13px;margin-bottom:0px;margin-top:10px"> + (Android Wear only) + </p> </div> <p>To call a taxi, use the @@ -1548,17 +1599,22 @@ public void playMedia(Uri file) { <h3 id="PlaySearch">Play music based on a search query</h3> -<!-- Google Now box --> -<div class="now-box"> - <div class="now-img-cont"> - <a href="#Now"> - <img src="{@docRoot}guide/components/images/voice-icon.png" class="now-img" - width="30" height="30" alt=""/> - </a> +<!-- Google Voice Actions box --> +<div class="voice-box"> + <div class="voice-img-cont"> + <a href= + "https://developers.google.com/voice-actions/system/#system_actions_reference"> + <img src="{@docRoot}guide/components/images/voice-icon.png" class= + "voice-img" width="30" height="30" alt=""></a> </div> - <p class="now-title">Google Now</p> + + <p class="voice-title"> + Google Voice Actions + </p> + <ul> - <li>"play michael jackson billie jean"</li> + <li>"play michael jackson billie jean" + </li> </ul> </div> @@ -1861,19 +1917,28 @@ android.content.Intent#ACTION_DIAL} action and specify a phone number using the URI scheme defined below. When the phone app opens, it displays the phone number but the user must press the <em>Call</em> button to begin the phone call.</p> -<!-- Google Now box --> -<div class="now-box"> - <div class="now-img-cont"> - <a href="#Now"> - <img src="{@docRoot}guide/components/images/voice-icon.png" class="now-img" - width="30" height="30" alt=""/> - </a> +<!-- Google Voice Actions box --> +<div class="voice-box"> + <div class="voice-img-cont"> + <a href= + "https://developers.google.com/voice-actions/system/#system_actions_reference"> + <img src="{@docRoot}guide/components/images/voice-icon.png" class= + "voice-img" width="30" height="30" alt=""></a> </div> - <p class="now-title">Google Now</p> + + <p class="voice-title"> + Google Voice Actions + </p> + <ul> - <li>"call 555-5555"</li> - <li>"call bob"</li> - <li>"call voicemail"</li> + <li>"call 555-5555" + </li> + + <li>"call bob" + </li> + + <li>"call voicemail" + </li> </ul> </div> @@ -1947,16 +2012,22 @@ public void dialPhoneNumber(String phoneNumber) { <h3 id="SearchOnApp">Search using a specific app</h3> -<!-- Google Now box --> -<div class="now-box"> - <div class="now-img-cont"> - <a href="#Now"> - <img src="{@docRoot}guide/components/images/voice-icon.png" class="now-img" - width="30" height="30"/></a> +<!-- Google Voice Actions box --> +<div class="voice-box"> + <div class="voice-img-cont"> + <a href= + "https://developers.google.com/voice-actions/system/#system_actions_reference"> + <img src="{@docRoot}guide/components/images/voice-icon.png" class= + "voice-img" width="30" height="30" alt=""></a> </div> - <p class="now-title">Google Now</p> + + <p class="voice-title"> + Google Voice Actions + </p> + <ul> - <li>"search for cat videos on myvideoapp"</li> + <li>"search for cat videos on myvideoapp" + </li> </ul> </div> <!-- Video box --> @@ -1976,7 +2047,7 @@ the <code>SEARCH_ACTION</code> action, as shown in the example intent filter bel <dd> <dl> <dt><code>"com.google.android.gms.actions.SEARCH_ACTION"</code></dt> - <dd>Support search queries from Google Now.</dd> + <dd>Support search queries from Google Voice Actions.</dd> </dl> </dd> @@ -2207,17 +2278,22 @@ at {@link android.provider.Telephony}.</p> <h3 id="ViewUrl">Load a web URL</h3> -<!-- Google Now box --> -<div class="now-box"> - <div class="now-img-cont"> - <a href="#Now"> - <img src="{@docRoot}guide/components/images/voice-icon.png" class="now-img" - width="30" height="30" alt=""/> - </a> +<!-- Google Voice Actions box --> +<div class="voice-box"> + <div class="voice-img-cont"> + <a href= + "https://developers.google.com/voice-actions/system/#system_actions_reference"> + <img src="{@docRoot}guide/components/images/voice-icon.png" class= + "voice-img" width="30" height="30" alt=""></a> </div> - <p class="now-title">Google Now</p> + + <p class="voice-title"> + Google Voice Actions + </p> + <ul> - <li>"open example.com"</li> + <li>"open example.com" + </li> </ul> </div> @@ -2307,128 +2383,4 @@ adb shell am start -a android.intent.action.DIAL \ <p>For more information, see -<a href="{@docRoot}tools/help/shell.html#am">ADB Shell Commands</a>.</p> - - - - - - -<h2 id="Now">Intents Fired by Google Now</h2> - -<p><a href="http://www.google.com/landing/now/">Google Now</a> recognizes many voice commands -and fires intents for them. As such, users may launch your app with a Google Now voice command -if your app declares the corresponding intent filter. For example, if your app can -<a href="#CreateAlarm">set an alarm</a> and you add the corresponding intent filter to your -manifest file, Google Now lets users choose your app when they request to set an alarm, as -shown in figure 1.</p> - -<img src="{@docRoot}guide/components/images/google-action.png" - srcset="{@docRoot}guide/components/images/google-action_2x.png 2x" - width="700" height="241" alt=""/> -<p class="img-caption"><strong>Figure 1.</strong> Google Now lets users choose from installed -apps that support a given action.</p> - -<p>Google Now recognizes voice commands for the actions listed in table 1. For more information -about declaring each intent filter, click on the action description.</p> - -<p class="table-caption"><strong>Table 1.</strong> Voice commands recognized by Google Now -(Google Search app v3.6).</p> -<table> -<tr> - <th>Category</th> - <th>Details and Examples</th> - <th>Action Name</th> -</tr> -<tr> - <td rowspan="2" style="vertical-align:middle">Alarm</td> - <td> - <p><a href="#CreateAlarm">Set alarm</a></p> - <ul class="now-list"> - <li>"set an alarm for 7 am"</li> - </ul> - </td> - <td>{@link android.provider.AlarmClock#ACTION_SET_ALARM AlarmClock.ACTION_SET_ALARM}</td> -</tr> -<tr> - <td> - <p><a href="#CreateTimer">Set timer</a></p> - <ul class="now-list"> - <li>"set a timer for 5 minutes"</li> - </ul> - </td> - <td>{@link android.provider.AlarmClock#ACTION_SET_TIMER AlarmClock.ACTION_SET_TIMER}</td> -</tr> -<tr> - <td style="vertical-align:middle">Communication</td> - <td> - <p><a href="#DialPhone">Call a number</a></p> - <ul class="now-list"> - <li>"call 555-5555"</li> - <li>"call bob"</li> - <li>"call voicemail"</li> - </ul> - </td> - <td>{@link android.content.Intent#ACTION_CALL Intent.ACTION_CALL}</td> -</tr> -<tr> - <td style="vertical-align:middle">Local</td> - <td> - <p><a href="#CallCar">Book a car</a></p> - <ul class="now-list"> - <li>"call me a car"</li> - <li>"book me a taxi"</li> - </ul> - </td> - <td><a href="{@docRoot}reference/com/google/android/gms/actions/ReserveIntents.html#ACTION_RESERVE_TAXI_RESERVATION"> - <code>ReserveIntents<br/>.ACTION_RESERVE_TAXI_RESERVATION</code></a></td> -</tr> -<tr> - <td rowspan="3" style="vertical-align:middle">Media</td> - <td> - <p><a href="#PlaySearch">Play music from search</a></p> - <ul class="now-list"> - <li>"play michael jackson billie jean"</li> - </ul> - </td> - <td>{@link android.provider.MediaStore#INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH MediaStore<br/>.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH}</td> -</tr> -<tr> - <td> - <p><a href="#CameraStill">Take a picture</a></p> - <ul class="now-list"> - <li>"take a picture"</li> - </ul> - </td> - <td>{@link android.provider.MediaStore#INTENT_ACTION_STILL_IMAGE_CAMERA MediaStore<br/>.INTENT_ACTION_STILL_IMAGE_CAMERA}</td> -</tr> -<tr> - <td> - <p><a href="#CameraVideo">Record a video</a></p> - <ul class="now-list"> - <li>"record a video"</li> - </ul> - </td> - <td>{@link android.provider.MediaStore#INTENT_ACTION_VIDEO_CAMERA MediaStore<br/>.INTENT_ACTION_VIDEO_CAMERA}</td> -</tr> -<tr> - <td style="vertical-align:middle">Search</td> - <td> - <p><a href="#SearchOnApp">Search using a specific app</a></p> - <ul class="now-list"> - <li>"search for cat videos <br/>on myvideoapp"</li> - </ul> - </td> - <td><code>"com.google.android.gms.actions<br/>.SEARCH_ACTION"</code></td> -</tr> -<tr> - <td style="vertical-align:middle">Web browser</td> - <td> - <p><a href="#ViewUrl">Open URL</a></p> - <ul class="now-list"> - <li>"open example.com"</li> - </ul> - </td> - <td>{@link android.content.Intent#ACTION_VIEW Intent.ACTION_VIEW}</td> -</tr> -</table> +<a href="{@docRoot}tools/help/shell.html#am">ADB Shell Commands</a>.</p>
\ No newline at end of file diff --git a/docs/html/guide/topics/renderscript/compute.jd b/docs/html/guide/topics/renderscript/compute.jd index fe686547fe6c..13880ec6b796 100755 --- a/docs/html/guide/topics/renderscript/compute.jd +++ b/docs/html/guide/topics/renderscript/compute.jd @@ -16,6 +16,13 @@ parent.link=index.html </ol> </li> <li><a href="#using-rs-from-java">Using RenderScript from Java Code</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> + <li><a href="#calling-reduction-kernel">Calling a reduction kernel from Java code</a></li> + <li><a href="#more-example">More example reduction kernels</a></li> + </ol> + </li> </ol> <h2>Related Samples</h2> @@ -29,16 +36,18 @@ parent.link=index.html <p>RenderScript is a framework for running computationally intensive tasks at high performance on Android. RenderScript is primarily oriented for use with data-parallel computation, although serial -computationally intensive workloads can benefit as well. The RenderScript runtime will parallelize -work across all processors available on a device, such as multi-core CPUs, GPUs, or DSPs, allowing -you to focus on expressing algorithms rather than scheduling work or load balancing. RenderScript is +workloads can benefit as well. The RenderScript runtime parallelizes +work across processors available on a device, such as multi-core CPUs and GPUs. This allows +you to focus on expressing algorithms rather than scheduling work. RenderScript is especially useful for applications performing image processing, computational photography, or 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.</li> +<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>A Java API is used for managing the lifetime of RenderScript resources and controlling kernel execution.</li> @@ -48,7 +57,7 @@ execution.</li> <p>A RenderScript kernel typically resides in a <code>.rs</code> file in the <code><project_root>/src/</code> directory; each <code>.rs</code> file is called a -script. Every script contains its own set of kernels, functions, and variables. A script can +<i>script</i>. Every script contains its own set of kernels, functions, and variables. A script can contain:</p> <ul> @@ -57,23 +66,32 @@ RenderScript kernel language used in this script. Currently, 1 is the only valid <li>A pragma declaration (<code>#pragma rs java_package_name(com.example.app)</code>) that declares the package name of the Java classes reflected from this script. -Note that your .rs file must be part of your application package, and not in a +Note that your <code>.rs</code> file must be part of your application package, and not in a library project.</li> -<li>Some number of invokable functions. An invokable function is a single-threaded RenderScript +<li>Zero or more <strong><i>invokable functions</i></strong>. An invokable function is a single-threaded RenderScript function that you can call from your Java code with arbitrary arguments. These are often useful for initial setup or serial computations within a larger processing pipeline.</li> -<li>Some number of script globals. A script global is equivalent to a global variable in C. You can +<li><p>Zero or more <strong><i>script globals</i></strong>. A script global is equivalent to a global variable in C. You can access script globals from Java code, and these are often used for parameter passing to RenderScript -kernels.</li> +kernels.</p></li> -<li>Some number of compute kernels. A kernel is a parallel function that executes across every -{@link android.renderscript.Element} within an {@link android.renderscript.Allocation}. +<li><p>Zero or more <strong><i>compute kernels</i></strong>. There are two kinds of compute +kernels: <i>mapping</i> kernels (also called <i>foreach</i> kernels) +and <i>reduction</i> kernels.</p> -<p>A simple kernel may look like the following:</p> +<p>A <em>mapping kernel</em> is a parallel function that operates on a collection of {@link + android.renderscript.Allocation Allocations} of the same dimensions. By default, it executes + once for every coordinate in those dimensions. It is typically (but not exclusively) used to + transform a collection of input {@link android.renderscript.Allocation Allocations} to an + output {@link android.renderscript.Allocation} one {@link android.renderscript.Element} at a + time.</p> -<pre>uchar4 __attribute__((kernel)) invert(uchar4 in, uint32_t x, uint32_t y) { +<ul> +<li><p>Here is an example of a simple <strong>mapping kernel</strong>:</p> + +<pre>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; @@ -81,40 +99,113 @@ kernels.</li> return out; }</pre> -<p>In most respects, this is identical to a standard C function. The first notable feature is the -<code>__attribute__((kernel))</code> applied to the function prototype. This denotes that the -function is a RenderScript kernel instead of an invokable function. The next feature is the -<code>in</code> argument and its type. In a RenderScript kernel, this is a special argument that is -automatically filled in based on the input {@link android.renderscript.Allocation} passed to the -kernel launch. By default, the kernel is run across an entire {@link -android.renderscript.Allocation}, with one execution of the kernel body per {@link -android.renderscript.Element} in the {@link android.renderscript.Allocation}. The third notable -feature is the return type of the kernel. The value returned from the kernel is automatically -written to the appropriate location in the output {@link android.renderscript.Allocation}. The -RenderScript runtime checks to ensure that the {@link android.renderscript.Element} types of the -input and output Allocations match the kernel's prototype; if they do not match, an exception is -thrown.</p> - -<p>A kernel may have an input {@link android.renderscript.Allocation}, an output {@link -android.renderscript.Allocation}, or both. A kernel may not have more than one input or one output -{@link android.renderscript.Allocation}. If more than one input or output is required, those objects -should be bound to <code>rs_allocation</code> script globals and accessed from a kernel or invokable -function via <code>rsGetElementAt_<em>type</em>()</code> or -<code>rsSetElementAt_<em>type</em>()</code>.</p> - -<p>A kernel may access the coordinates of the current execution using the <code>x</code>, -<code>y</code>, and <code>z</code> arguments. These arguments are optional, but the type of the -coordinate arguments must be <code>uint32_t</code>.</p></li> +<p>In most respects, this is identical to a standard C + function. The <a href="#RS_KERNEL"><code>RS_KERNEL</code></a> property applied to the + function prototype specifies that the function is a RenderScript mapping kernel instead of an + invokable function. The <code>in</code> argument is automatically filled in based on the + input {@link android.renderscript.Allocation} passed to the kernel launch. The + arguments <code>x</code> and <code>y</code> are + discussed <a href="#special-arguments">below</a>. The value returned from the kernel is + automatically written to the appropriate location in the output {@link + android.renderscript.Allocation}. By default, this kernel is run across its entire input + {@link android.renderscript.Allocation}, with one execution of the kernel function per {@link + android.renderscript.Element} in the {@link android.renderscript.Allocation}.</p> + +<p>A mapping kernel may have one or more input {@link android.renderscript.Allocation + Allocations}, a single output {@link android.renderscript.Allocation}, or both. The + RenderScript runtime checks to ensure that all input and output Allocations have the same + dimensions, and that the {@link android.renderscript.Element} types of the input and output + Allocations match the kernel's prototype; if either of these checks fails, RenderScript + throws an exception.</p> + +<p class="note"><strong>NOTE:</strong> Before Android 6.0 (API level 23), a mapping kernel may + not have more than one input {@link android.renderscript.Allocation}.</p> + +<p>If you need more input or output {@link android.renderscript.Allocation Allocations} than + the kernel has, those objects should be bound to <code>rs_allocation</code> script globals + and accessed from a kernel or invokable function + via <code>rsGetElementAt_<i>type</i>()</code> or <code>rsSetElementAt_<i>type</i>()</code>.</p> + +<p><strong>NOTE:</strong> <a id="RS_KERNEL"><code>RS_KERNEL</code></a> is a macro + defined automatically by RenderScript for your convenience:</p> +<pre> +#define RS_KERNEL __attribute__((kernel)) +</pre> +</li> +</ul> + +<p>A <em>reduction kernel</em> is a family of functions that operates on a collection of input + {@link android.renderscript.Allocation Allocations} of the same dimensions. By default, + its <a href="#accumulator-function">accumulator function</a> executes once for every + coordinate in those dimensions. It is typically (but not exclusively) used to "reduce" a + collection of input {@link android.renderscript.Allocation Allocations} to a single + value.</p> + +<ul> +<li><p>Here is an <a id="example-addint">example</a> of a simple <strong>reduction +kernel</strong> that adds up the {@link android.renderscript.Element Elements} of its +input:</p> + +<pre>#pragma rs reduce(addint) accumulator(addintAccum) + +static void addintAccum(int *accum, int val) { + *accum += val; +}</pre> + +<p>A reduction kernel consists of one or more user-written functions. +<code>#pragma rs reduce</code> is used to define the kernel by specifying its name +(<code>addint</code>, in this example) and the names and roles of the functions that make +up the kernel (an <code>accumulator</code> function <code>addintAccum</code>, in this +example). All such functions must be <code>static</code>. A reduction kernel always +requires an <code>accumulator</code> function; it may also have other functions, depending +on what you want the kernel to do.</p> + +<p>A reduction kernel accumulator function must return <code>void</code> and must have at least +two arguments. The first argument (<code>accum</code>, in this example) is a pointer to +an <i>accumulator data item</i> and the second (<code>val</code>, in this example) is +automatically filled in based on the input {@link android.renderscript.Allocation} passed to +the kernel launch. The accumulator data item is created by the RenderScript runtime; by +default, it is initialized to zero. By default, this kernel is run across its entire input +{@link android.renderscript.Allocation}, with one execution of the accumulator function per +{@link android.renderscript.Element} in the {@link android.renderscript.Allocation}. By +default, the final value of the accumulator data item is treated as the result of the +reduction, and is returned to Java. The RenderScript runtime checks to ensure that the {@link +android.renderscript.Element} type of the input Allocation matches the accumulator function's +prototype; if it does not match, RenderScript throws an exception.</p> + +<p>A reduction kernel has one or more input {@link android.renderscript.Allocation +Allocations} but no output {@link android.renderscript.Allocation Allocations}.</p></li> + +<p>Reduction kernels are explained in more detail <a href="#reduction-in-depth">here</a>.</p> + +<p>Reduction kernels are supported in Android 7.0 (API level 24) and later.</p> +</li> +</ul> + +<p>A mapping kernel function or a reduction kernel accumulator function may access the coordinates +of the current execution using the <a id="special-arguments">special arguments</a> <code>x</code>, +<code>y</code>, and <code>z</code>, which must be of type <code>int</code> or <code>uint32_t</code>. +These arguments are optional.</p> + +<p>A mapping kernel function or a reduction kernel accumulator +function may also take the optional special argument +<code>context</code> of type <a +href='reference/rs_for_each.html#android_rs:rs_kernel_context'>rs_kernel_context</a>. +It is needed by a family of runtime APIs that are used to query +certain properties of the current execution -- for example, <a +href='reference/rs_for_each.html#android_rs:rsGetDimX'>rsGetDimX</a>. +(The <code>context</code> argument is available in Android 6.0 (API level 23) and later.)</p> +</li> <li>An optional <code>init()</code> function. An <code>init()</code> function is a special type of -invokable function that is run when the script is first instantiated. This allows for some +invokable function that RenderScript runs when the script is first instantiated. This allows for some computation to occur automatically at script creation.</li> -<li>Some number of static script globals and functions. A static script global is equivalent to a -script global except that it cannot be set from Java code. A static function is a standard C +<li>Zero or more <strong><i>static script globals and functions</i></strong>. A static script global is equivalent to a +script global except that it cannot be accessed from Java code. A static function is a standard C function that can be called from any kernel or invokable function in the script but is not exposed to the Java API. If a script global or function does not need to be called from Java code, it is -highly recommended that those be declared <code>static</code>.</li> </ul> +highly recommended that it be declared <code>static</code>.</li> </ul> <h4>Setting floating point precision</h4> @@ -129,13 +220,13 @@ different level of floating point precision:</p> </li> - <li><code>#pragma rs_fp_relaxed</code> - For apps that don’t require strict IEEE 754-2008 + <li><code>#pragma rs_fp_relaxed</code>: For apps that don’t require strict IEEE 754-2008 compliance and can tolerate less precision. This mode enables flush-to-zero for denorms and round-towards-zero. </li> - <li><code>#pragma rs_fp_imprecise</code> - For apps that don’t have stringent precision + <li><code>#pragma rs_fp_imprecise</code>: For apps that don’t have stringent precision requirements. This mode enables everything in <code>rs_fp_relaxed</code> along with the following: @@ -162,14 +253,21 @@ precision (such as SIMD CPU instructions).</p> available on devices running Android 3.0 (API level 11) and higher. </li> <li><strong>{@link android.support.v8.renderscript}</strong> - The APIs in this package are available through a <a href="{@docRoot}tools/support-library/features.html#v8">Support - Library</a>, which allows you to use them on devices running Android 2.2 (API level 8) and + Library</a>, which allows you to use them on devices running Android 2.3 (API level 9) and higher.</li> </ul> -<p>We strongly recommend using the Support Library APIs for accessing RenderScript because they - provide a wider range of device compatibility. Developers targeting specific versions of - Android can use {@link android.renderscript} if necessary.</p> +<p>Here are the tradeoffs:</p> +<ul> +<li>If you use the Support Library APIs, the RenderScript portion of your application will be + compatible with devices running Android 2.3 (API level 9) and higher, regardless of which RenderScript + features you use. This allows your application to work on more devices than if you use the + native (<strong>{@link android.renderscript}</strong>) APIs.</li> +<li>Certain RenderScript features are not available through the Support Library APIs.</li> +<li>If you use the Support Library APIs, you will get (possibly significantly) larger APKs than +if you use the native (<strong>{@link android.renderscript}</strong>) APIs.</li> +</ul> <h3 id="ide-setup">Using the RenderScript Support Library APIs</h3> @@ -202,7 +300,7 @@ android { buildToolsVersion "23.0.3" defaultConfig { - minSdkVersion 8 + minSdkVersion 9 targetSdkVersion 19 <strong> renderscriptTargetApi 18 @@ -250,7 +348,7 @@ import android.support.v8.renderscript.*; <p>Using RenderScript from Java code relies on the API classes located in the {@link android.renderscript} or the {@link android.support.v8.renderscript} package. Most -applications follow the same basic usage patterns:</p> +applications follow the same basic usage pattern:</p> <ol> @@ -266,12 +364,12 @@ possible. Typically, an application will have only a single RenderScript context script.</strong> An {@link android.renderscript.Allocation} is a RenderScript object that provides storage for a fixed amount of data. Kernels in scripts take {@link android.renderscript.Allocation} objects as their input and output, and {@link android.renderscript.Allocation} objects can be -accessed in kernels using <code>rsGetElementAt_<em>type</em>()</code> and -<code>rsSetElementAt_<em>type</em>()</code> when bound as script globals. {@link +accessed in kernels using <code>rsGetElementAt_<i>type</i>()</code> and +<code>rsSetElementAt_<i>type</i>()</code> when bound as script globals. {@link android.renderscript.Allocation} objects allow arrays to be passed from Java code to RenderScript code and vice-versa. {@link android.renderscript.Allocation} objects are typically created using -{@link android.renderscript.Allocation#createTyped} or {@link -android.renderscript.Allocation#createFromBitmap}.</li> +{@link android.renderscript.Allocation#createTyped createTyped()} or {@link +android.renderscript.Allocation#createFromBitmap createFromBitmap()}.</li> <li><strong>Create whatever scripts are necessary.</strong> There are two types of scripts available to you when using RenderScript: @@ -281,9 +379,9 @@ to you when using RenderScript: <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 reflected by the RenderScript compiler in order to make it easy to access the script from Java code; -this class will have the name <code>ScriptC_<em>filename</em></code>. For example, if the kernel -above was located in <code>invert.rs</code> and a RenderScript context was already located in -<code>mRS</code>, the Java code to instantiate the script would be: +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 +<code>mRenderScript</code>, the Java code to instantiate the script would be: <pre>ScriptC_invert invert = new ScriptC_invert(mRenderScript);</pre></li> @@ -294,35 +392,926 @@ such as Gaussian blur, convolution, and image blending. For more information, se </ul></li> <li><strong>Populate Allocations with data.</strong> Except for Allocations created with {@link -android.renderscript.Allocation#createFromBitmap}, an Allocation will be populated with empty data when it is -first created. To populate an Allocation, use one of the <code>copy</code> methods in {@link -android.renderscript.Allocation}.</li> - -<li><strong>Set any necessary script globals.</strong> Globals may be set using methods in the same -<code>ScriptC_<em>filename</em></code> class with methods named -<code>set_<em>globalname</em></code>. For example, in order to set an <code>int</code> named -<code>elements</code>, use the Java method <code>set_elements(int)</code>. RenderScript objects can -also be set in kernels; for example, the <code>rs_allocation</code> variable named -<code>lookup</code> can be set with the method <code>set_lookup(Allocation)</code>.</li> - -<li><strong>Launch the appropriate kernels.</strong> Methods to launch a given kernel will be -reflected in the same <code>ScriptC_<em>filename</em></code> class with methods named -<code>forEach_<em>kernelname</em>()</code>. These launches are asynchronous, and launches will be -serialized in the order in which they are launched. Depending on the arguments to the kernel, the -method will take either one or two Allocations. By default, a kernel will execute over the entire -input or output Allocation; to execute over a subset of that Allocation, pass an appropriate {@link -android.renderscript.Script.LaunchOptions} as the last argument to the <code>forEach</code> method. - -<p>Invoked functions can be launched using the <code>invoke_<em>functionname</em></code> methods -reflected in the same <code>ScriptC_<em>filename</em></code> class.</p></li> - -<li><strong>Copy data out of {@link android.renderscript.Allocation} objects.</strong> In order to -access data from an {@link android.renderscript.Allocation} from Java code, that data must be copied -back to Java buffers using one of the <code>copy</code> methods in {@link -android.renderscript.Allocation}. These functions will synchronize with asynchronous kernel and -function launches as necessary.</li> - -<li><strong>Tear down the RenderScript context.</strong> The RenderScript context can be destroyed +android.renderscript.Allocation#createFromBitmap createFromBitmap()}, an Allocation is populated with empty data when it is +first created. To populate an Allocation, use one of the "copy" methods in {@link +android.renderscript.Allocation}. The "copy" methods are <a href="#asynchronous-model">synchronous</a>.</li> + +<li><strong>Set any necessary script globals.</strong> You may set globals using methods in the + same <code>ScriptC_<i>filename</i></code> class named <code>set_<i>globalname</i></code>. For + example, in order to set an <code>int</code> variable named <code>threshold</code>, use the + Java method <code>set_threshold(int)</code>; and in order to set + an <code>rs_allocation</code> variable named <code>lookup</code>, use the Java + method <code>set_lookup(Allocation)</code>. The <code>set</code> methods + are <a href="#asynchronous-model">asynchronous</a>.</li> + +<li><strong>Launch the appropriate kernels and invokable functions.</strong> +<p>Methods to launch a given kernel are +reflected in the same <code>ScriptC_<i>filename</i></code> class with methods named +<code>forEach_<i>mappingKernelName</i>()</code> +or <code>reduce_<i>reductionKernelName</i>()</code>. +These launches are <a href="#asynchronous-model">asynchronous</a>. +Depending on the arguments to the kernel, the +method takes one or more Allocations, all of which must have the same dimensions. By default, a +kernel executes over every coordinate in those dimensions; to execute a kernel over a subset of those coordinates, +pass an appropriate {@link +android.renderscript.Script.LaunchOptions} as the last argument to the <code>forEach</code> or <code>reduce</code> method.</p> + +<p>Launch invokable functions using the <code>invoke_<i>functionName</i></code> methods +reflected in the same <code>ScriptC_<i>filename</i></code> class. +These launches are <a href="#asynchronous-model">asynchronous</a>.</p></li> + +<li><strong>Retrieve data from {@link android.renderscript.Allocation} objects +and <i><a href="#javaFutureType">javaFutureType</a></i> objects.</strong> +In order to +access data from an {@link android.renderscript.Allocation} from Java code, you must copy that data +back to Java using one of the "copy" methods in {@link +android.renderscript.Allocation}. +In order to obtain the result of a reduction kernel, you must use the <code><i>javaFutureType</i>.get()</code> method. +The "copy" and <code>get()</code> methods are <a href="#asynchronous-model">synchronous</a>.</li> + +<li><strong>Tear down the RenderScript context.</strong> You can destroy the RenderScript context with {@link android.renderscript.RenderScript#destroy} or by allowing the RenderScript context -object to be garbage collected. This will cause any further use of any object belonging to that +object to be garbage collected. This causes any further use of any object belonging to that context to throw an exception.</li> </ol> + +<h3 id="asynchronous-model">Asynchronous execution model</h3> + +<p>The reflected <code>forEach</code>, <code>invoke</code>, <code>reduce</code>, + and <code>set</code> methods are asynchronous -- each may return to Java before completing the + requested action. However, the individual actions are serialized in the order in which they are launched.</p> + +<p>The {@link android.renderscript.Allocation} class provides "copy" methods to copy data to + and from Allocations. A "copy" method is synchronous, and is serialized with respect to any + of the asynchronous actions above that touch the same Allocation.</p> + +<p>The reflected <i><a href="#javaFutureType">javaFutureType</a></i> classes provide + 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="reduction-in-depth">Reduction Kernels in Depth</h2> + +<p><i>Reduction</i> is the process of combining a collection of data into a single +value. This is a useful primitive in parallel programming, with applications such as the +following:</p> +<ul> + <li>computing the sum or product over all the data</li> + <li>computing logical operations (<code>and</code>, <code>or</code>, <code>xor</code>) + over all the data</li> + <li>finding the minimum or maximum value within the data</li> + <li>searching for a specific value or for the coordinate of a specific value within the data</li> +</ul> + +<p>In Android 7.0 (API level 24) and later, RenderScript supports <i>reduction kernels</i> to allow +efficient user-written reduction algorithms. You may launch reduction kernels on inputs with +1, 2, or 3 dimensions.<p> + +<p>An example above shows a simple <a href="#example-addint">addint</a> reduction kernel. +Here is a more complicated <a id="example-findMinAndMax">findMinAndMax</a> reduction kernel +that finds the locations of the minimum and maximum <code>long</code> values in a +1-dimensional {@link android.renderscript.Allocation}:</p> + +<pre> +#define LONG_MAX (long)((1UL << 63) - 1) +#define LONG_MIN (long)(1UL << 63) + +#pragma rs reduce(findMinAndMax) \ + initializer(fMMInit) accumulator(fMMAccumulator) \ + combiner(fMMCombiner) outconverter(fMMOutConverter) + +// Either a value and the location where it was found, or <a href="#INITVAL">INITVAL</a>. +typedef struct { + long val; + int idx; // -1 indicates <a href="#INITVAL">INITVAL</a> +} IndexedVal; + +typedef struct { + IndexedVal min, max; +} MinAndMax; + +// In discussion below, this initial value { { LONG_MAX, -1 }, { LONG_MIN, -1 } } +// is called <a id="INITVAL">INITVAL</a>. +static void fMMInit(MinAndMax *accum) { + accum->min.val = LONG_MAX; + accum->min.idx = -1; + accum->max.val = LONG_MIN; + accum->max.idx = -1; +} + +//---------------------------------------------------------------------- +// In describing the behavior of the accumulator and combiner functions, +// it is helpful to describe hypothetical functions +// IndexedVal min(IndexedVal a, IndexedVal b) +// IndexedVal max(IndexedVal a, IndexedVal b) +// MinAndMax minmax(MinAndMax a, MinAndMax b) +// MinAndMax minmax(MinAndMax accum, IndexedVal val) +// +// The effect of +// IndexedVal min(IndexedVal a, IndexedVal b) +// is to return the IndexedVal from among the two arguments +// whose val is lesser, except that when an IndexedVal +// has a negative index, that IndexedVal is never less than +// any other IndexedVal; therefore, if exactly one of the +// two arguments has a negative index, the min is the other +// argument. Like ordinary arithmetic min and max, this function +// is commutative and associative; that is, +// +// min(A, B) == min(B, A) // commutative +// min(A, min(B, C)) == min((A, B), C) // associative +// +// The effect of +// IndexedVal max(IndexedVal a, IndexedVal b) +// is analogous (greater . . . never greater than). +// +// Then there is +// +// MinAndMax minmax(MinAndMax a, MinAndMax b) { +// return MinAndMax(min(a.min, b.min), max(a.max, b.max)); +// } +// +// Like ordinary arithmetic min and max, the above function +// is commutative and associative; that is: +// +// minmax(A, B) == minmax(B, A) // commutative +// minmax(A, minmax(B, C)) == minmax((A, B), C) // associative +// +// Finally define +// +// MinAndMax minmax(MinAndMax accum, IndexedVal val) { +// return minmax(accum, MinAndMax(val, val)); +// } +//---------------------------------------------------------------------- + +// This function can be explained as doing: +// *accum = minmax(*accum, IndexedVal(in, x)) +// +// This function simply computes minimum and maximum values as if +// INITVAL.min were greater than any other minimum value and +// INITVAL.max were less than any other maximum value. Note that if +// *accum is INITVAL, then this function sets +// *accum = IndexedVal(in, x) +// +// After this function is called, both accum->min.idx and accum->max.idx +// will have nonnegative values: +// - x is always nonnegative, so if this function ever sets one of the +// idx fields, it will set it to a nonnegative value +// - if one of the idx fields is negative, then the corresponding +// val field must be LONG_MAX or LONG_MIN, so the function will always +// set both the val and idx fields +static void fMMAccumulator(MinAndMax *accum, long in, int x) { + IndexedVal me; + me.val = in; + me.idx = x; + + if (me.val <= accum->min.val) + accum->min = me; + if (me.val >= accum->max.val) + accum->max = me; +} + +// This function can be explained as doing: +// *accum = minmax(*accum, *val) +// +// This function simply computes minimum and maximum values as if +// INITVAL.min were greater than any other minimum value and +// INITVAL.max were less than any other maximum value. Note that if +// one of the two accumulator data items is INITVAL, then this +// function sets *accum to the other one. +static void fMMCombiner(MinAndMax *accum, + const MinAndMax *val) { + if ((accum->min.idx < 0) || (val->min.val < accum->min.val)) + accum->min = val->min; + if ((accum->max.idx < 0) || (val->max.val > accum->max.val)) + accum->max = val->max; +} + +static void fMMOutConverter(int2 *result, + const MinAndMax *val) { + result->x = val->min.idx; + result->y = val->max.idx; +} +</pre> + +<p class="note"><strong>NOTE:</strong> There are more example reduction + kernels <a href="#more-example">here</a>.</p> + +<p>In order to run a reduction kernel, the RenderScript runtime creates <em>one or more</em> +variables called <a id="accumulator-data-items"><strong><i>accumulator data +items</i></strong></a> to hold the state of the reduction process. The RenderScript runtime +picks the number of accumulator data items in such a way as to maximize performance. The type +of the accumulator data items (<i>accumType</i>) is determined by the kernel's <i>accumulator +function</i> -- the first argument to that function is a pointer to an accumulator data +item. By default, every accumulator data item is initialized to zero (as if +by <code>memset</code>); however, you may write an <i>initializer function</i> to do something +different.</p> + +<p class="note"><strong>Example:</strong> In the <a href="#example-addint">addint</a> +kernel, the accumulator data items (of type <code>int</code>) are used to add up input +values. There is no initializer function, so each accumulator data item is initialized to +zero.</p> + +<p class="note"><strong>Example:</strong> In +the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, the accumulator data items +(of type <code>MinAndMax</code>) are used to keep track of the minimum and maximum values +found so far. There is an initializer function to set these to <code>LONG_MAX</code> and +<code>LONG_MIN</code>, respectively; and to set the locations of these values to -1, indicating that +the values are not actually present in the (empty) portion of the input that has been +processed.</p> + +<p>RenderScript calls your accumulator function once for every coordinate in the +input(s). Typically, your function should update the accumulator data item in some way +according to the input.</p> + +<p class="note"><strong>Example:</strong> In the <a href="#example-addint">addint</a> +kernel, the accumulator function adds the value of an input Element to the accumulator +data item.</p> + +<p class="note"><strong>Example:</strong> In +the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, the accumulator function +checks to see whether the value of an input Element is less than or equal to the minimum +value recorded in the accumulator data item and/or greater than or equal to the maximum +value recorded in the accumulator data item, and updates the accumulator data item +accordingly.</p> + +<p>After the accumulator function has been called once for every coordinate in the input(s), +RenderScript must <strong>combine</strong> the <a href="#accumulator-data-items">accumulator +data items</a> together into a single accumulator data item. You may write a <i>combiner +function</i> to do this. If the accumulator function has a single input and +no <a href="#special-arguments">special arguments</a>, then you do not need to write a combiner +function; RenderScript will use the accumulator function to combine the accumulator data +items. (You may still write a combiner function if this default behavior is not what you +want.)</p> + +<p class="note"><strong>Example:</strong> In the <a href="#example-addint">addint</a> +kernel, there is no combiner function, so the accumulator function will be used. This is +the correct behavior, because if we split a collection of values into two pieces, and we +add up the values in those two pieces separately, adding up those two sums is the same as +adding up the entire collection.</p> + +<p class="note"><strong>Example:</strong> In +the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, the combiner function +checks to see whether the minimum value recorded in the "source" accumulator data +item <code>*val</code> is less then the minimum value recorded in the "destination" +accumulator data item <code>*accum</code>, and updates <code>*accum</code> +accordingly. It does similar work for the maximum value. This updates <code>*accum</code> +to the state it would have had if all of the input values had been accumulated into +<code>*accum</code> rather than some into <code>*accum</code> and some into +<code>*val</code>.</p> + +<p>After all of the accumulator data items have been combined, RenderScript determines +the result of the reduction to return to Java. You may write an <i>outconverter +function</i> to do this. You do not need to write an outconverter function if you want +the final value of the combined accumulator data items to be the result of the reduction.</p> + +<p class="note"><strong>Example:</strong> In the <a href="#example-addint">addint</a> kernel, +there is no outconverter function. The final value of the combined data items is the sum of +all Elements of the input, which is the value we want to return.</p> + +<p class="note"><strong>Example:</strong> In +the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, the outconverter function +initializes an <code>int2</code> result value to hold the locations of the minimum and +maximum values resulting from the combination of all of the accumulator data items.</p> + +<h3 id="writing-reduction-kernel">Writing a reduction kernel</h3> + +<p><code>#pragma rs reduce</code> defines a reduction kernel by +specifying its name and the names and roles of the functions that make +up the kernel. All such functions must be +<code>static</code>. A reduction kernel always requires an <code>accumulator</code> +function; you can omit some or all of the other functions, depending on what you want the +kernel to do.</p> + +<pre>#pragma rs reduce(<i>kernelName</i>) \ + initializer(<i>initializerName</i>) \ + accumulator(<i>accumulatorName</i>) \ + combiner(<i>combinerName</i>) \ + outconverter(<i>outconverterName</i>) +</pre> + +<p>The meaning of the items in the <code>#pragma</code> is as follows:</p> +<ul> + +<li><code>reduce(<i>kernelName</i>)</code> (mandatory): Specifies that a reduction kernel is +being defined. A reflected Java method <code>reduce_<i>kernelName</i></code> will launch the +kernel.</li> + +<li><p><code>initializer(<i>initializerName</i>)</code> (optional): Specifies the name of the +initializer function for this reduction kernel. When you launch the kernel, RenderScript calls +this function once for each <a href="#accumulator-data-items">accumulator data item</a>. The +function must be defined like this:</p> + +<pre>static void <i>initializerName</i>(<i>accumType</i> *accum) { … }</pre> + +<p><code>accum</code> is a pointer to an accumulator data item for this function to +initialize.</p> + +<p>If you do not provide an initializer function, RenderScript initializes every accumulator +data item to zero (as if by <code>memset</code>), behaving as if there were an initializer +function that looks like this:</p> +<pre>static void <i>initializerName</i>(<i>accumType</i> *accum) { + memset(accum, 0, sizeof(*accum)); +}</pre> +</li> + +<li><p><code><a id="accumulator-function">accumulator(<i>accumulatorName</i>)</a></code> +(mandatory): Specifies the name of the accumulator function for this +reduction kernel. When you launch the kernel, RenderScript calls +this function once for every coordinate in the input(s), to update an +accumulator data item in some way according to the input(s). The function +must be defined like this:</p> + +<pre> +static void <i>accumulatorName</i>(<i>accumType</i> *accum, + <i>in1Type</i> in1, <i>…,</i> <i>inNType</i> in<i>N</i> + <i>[, specialArguments]</i>) { … } +</pre> + +<p><code>accum</code> is a pointer to an accumulator data item for this function to +modify. <code>in1</code> through <code>in<i>N</i></code> are one <em>or more</em> arguments that +are automatically filled in based on the inputs passed to the kernel launch, one argument +per input. The accumulator function may optionally take any of the <a +href="#special-arguments">special arguments</a>.</p> + +<p>An example kernel with multiple inputs is <a href="#dot-product"><code>dotProduct</code></a>.</p> +</li> + +<li><code><a id="combiner-function">combiner(<i>combinerName</i>)</a></code> +(optional): Specifies the name of the combiner function for this +reduction kernel. After RenderScript calls the accumulator function +once for every coordinate in the input(s), it calls this function as many +times as necessary to combine all accumulator data items into a single +accumulator data item. The function must be defined like this:</p> + +<pre>static void <i>combinerName</i>(<i>accumType</i> *accum, const <i>accumType</i> *other) { … }</pre> + +<p><code>accum</code> is a pointer to a "destination" accumulator data item for this +function to modify. <code>other</code> is a pointer to a "source" accumulator data item +for this function to "combine" into <code>*accum</code>.</p> + +<p class="note"><strong>NOTE:</strong> It is possible + that <code>*accum</code>, <code>*other</code>, or both have been initialized but have never + been passed to the accumulator function; that is, one or both have never been updated + according to any input data. For example, in + the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, the combiner + function <code>fMMCombiner</code> explicitly checks for <code>idx < 0</code> because that + indicates such an accumulator data item, whose value is <a href="#INITVAL">INITVAL</a>.</p> + +<p>If you do not provide a combiner function, RenderScript uses the accumulator function in its +place, behaving as if there were a combiner function that looks like this:</p> + +<pre>static void <i>combinerName</i>(<i>accumType</i> *accum, const <i>accumType</i> *other) { + <i>accumulatorName</i>(accum, *other); +}</pre> + +<p>A combiner function is mandatory if the kernel has more than one input, if the input data + type is not the same as the accumulator data type, or if the accumulator function takes one + or more <a href="#special-arguments">special arguments</a>.</p> +</li> + +<li><p><code><a id="outconverter-function">outconverter(<i>outconverterName</i>)</a></code> +(optional): Specifies the name of the outconverter function for this +reduction kernel. After RenderScript combines all of the accumulator +data items, it calls this function to determine the result of the +reduction to return to Java. The function must be defined like +this:</p> + +<pre>static void <i>outconverterName</i>(<i>resultType</i> *result, const <i>accumType</i> *accum) { … }</pre> + +<p><code>result</code> is a pointer to a result data item (allocated but not initialized +by the RenderScript runtime) for this function to initialize with the result of the +reduction. <i>resultType</i> is the type of that data item, which need not be the same +as <i>accumType</i>. <code>accum</code> is a pointer to the final accumulator data item +computed by the <a href="#combiner-function">combiner function</a>.</p> + +<p>If you do not provide an outconverter function, RenderScript copies the final accumulator +data item to the result data item, behaving as if there were an outconverter function that +looks like this:</p> + +<pre>static void <i>outconverterName</i>(<i>accumType</i> *result, const <i>accumType</i> *accum) { + *result = *accum; +}</pre> + +<p>If you want a different result type than the accumulator data type, then the outconverter function is mandatory.</p> +</li> + +</ul> + +<p>Note that a kernel has input types, an accumulator data item type, and a result type, + none of which need to be the same. For example, in + the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, the input + type <code>long</code>, accumulator data item type <code>MinAndMax</code>, and result + type <code>int2</code> are all different.</p> + +<h4 id="assume">What can't you assume?</h4> + +<p>You must not rely on the number of accumulator data items created by RenderScript for a + given kernel launch. There is no guarantee that two launches of the same kernel with the + same input(s) will create the same number of accumulator data items.</p> + +<p>You must not rely on the order in which RenderScript calls the initializer, accumulator, and + combiner functions; it may even call some of them in parallel. There is no guarantee that + two launches of the same kernel with the same input will follow the same order. The only + guarantee is that only the initializer function will ever see an uninitialized accumulator + data item. For example:</p> +<ul> +<li>There is no guarantee that all accumulator data items will be initialized before the + accumulator function is called, although it will only be called on an initialized accumulator + data item.</li> +<li>There is no guarantee on the order in which input Elements are passed to the accumulator + function.</li> +<li>There is no guarantee that the accumulator function has been called for all input Elements + before the combiner function is called.</li> +</ul> + +<p>One consequence of this is that the <a href="#example-findMinAndMax">findMinAndMax</a> + kernel is not deterministic: If the input contains more than one occurrence of the same + minimum or maximum value, you have no way of knowing which occurrence the kernel will + find.</p> + +<h4 id="guarantee">What must you guarantee?</h4> + +<p>Because the RenderScript system can choose to execute a kernel <a href="#assume">in many + different ways</a>, you must follow certain rules to ensure that your kernel behaves the + way you want. If you do not follow these rules, you may get incorrect results, + nondeterministic behavior, or runtime errors.</p> + +<p>The rules below often say that two accumulator data items must have "<a id="the-same">the + same value"</a>. What does this mean? That depends on what you want the kernel to do. For + a mathematical reduction such as <a href="#example-addint">addint</a>, it usually makes sense + for "the same" to mean mathematical equality. For a "pick any" search such + as <a href="#example-findMinAndMax">findMinAndMax</a> ("find the location of minimum and + maximum input values") where there might be more than one occurrence of identical input + values, all locations of a given input value must be considered "the same". You could write + a similar kernel to "find the location of <em>leftmost</em> minimum and maximum input values" + where (say) a minimum value at location 100 is preferred over an identical minimum value at location + 200; for this kernel, "the same" would mean identical <em>location</em>, not merely + identical <em>value</em>, and the accumulator and combiner functions would have to be + different than those for <a href="#example-findMinAndMax">findMinAndMax</a>.</p> + +<strong>The initializer function must create an <i>identity value</i>.</strong> That is, + if <code><i>I</i></code> and <code><i>A</i></code> are accumulator data items initialized + by the initializer function, and <code><i>I</i></code> has never been passed to the + accumulator function (but <code><i>A</i></code> may have been), then +<ul> +<li><code><i>combinerName</i>(&<i>A</i>, &<i>I</i>)</code> must + leave <code><i>A</i></code> <a href="#the-same">the same</a></li> +<li><code><i>combinerName</i>(&<i>I</i>, &<i>A</i>)</code> must + leave <code><i>I</i></code> <a href="#the-same">the same</a> as <code><i>A</i></code></li> +</ul> +<p class="note"><strong>Example:</strong> In the <a href="#example-addint">addint</a> + kernel, an accumulator data item is initialized to zero. The combiner function for this + kernel performs addition; zero is the identity value for addition.</p> +<div class="note"> +<p><strong>Example:</strong> In the <a href="#example-findMinAndMax">findMinAndMax</a> + kernel, an accumulator data item is initialized + to <a href="#INITVAL"><code>INITVAL</code></a>. +<ul> +<li><code>fMMCombiner(&<i>A</i>, &<i>I</i>)</code> leaves <code><i>A</i></code> the same, + because <code><i>I</i></code> is <code>INITVAL</code>.</li> +<li><code>fMMCombiner(&<i>I</i>, &<i>A</i>)</code> sets <code><i>I</i></code> + to <code><i>A</i></code>, because <code><i>I</i></code> is <code>INITVAL</code>.</li> +</ul> +Therefore, <code>INITVAL</code> is indeed an identity value. +</p></div> + +<p><strong>The combiner function must be <i>commutative</i>.</strong> That is, + if <code><i>A</i></code> and <code><i>B</i></code> are accumulator data items initialized + by the initializer function, and that may have been passed to the accumulator function zero + or more times, then <code><i>combinerName</i>(&<i>A</i>, &<i>B</i>)</code> must + set <code><i>A</i></code> to <a href="#the-same">the same value</a> + that <code><i>combinerName</i>(&<i>B</i>, &<i>A</i>)</code> + sets <code><i>B</i></code>.</p> +<p class="note"><strong>Example:</strong> In the <a href="#example-addint">addint</a> + kernel, the combiner function adds the two accumulator data item values; addition is + commutative.</p> +<div class="note"> +<p><strong>Example:</strong> In the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, +<pre> +fMMCombiner(&<i>A</i>, &<i>B</i>) +</pre> +is the same as +<pre> +<i>A</i> = minmax(<i>A</i>, <i>B</i>) +</pre> +and <code>minmax</code> is commutative, so <code>fMMCombiner</code> is also. +</p> +</div> + +<p><strong>The combiner function must be <i>associative</i>.</strong> That is, + if <code><i>A</i></code>, <code><i>B</i></code>, and <code><i>C</i></code> are + accumulator data items initialized by the initializer function, and that may have been passed + to the accumulator function zero or more times, then the following two code sequences must + set <code><i>A</i></code> to <a href="#the-same">the same value</a>:</p> +<ul> +<li><pre> +<i>combinerName</i>(&<i>A</i>, &<i>B</i>); +<i>combinerName</i>(&<i>A</i>, &<i>C</i>); +</pre></li> +<li><pre> +<i>combinerName</i>(&<i>B</i>, &<i>C</i>); +<i>combinerName</i>(&<i>A</i>, &<i>B</i>); +</pre></li> +</ul> +<div class="note"> +<p><strong>Example:</strong> In the <a href="#example-addint">addint</a> kernel, the + combiner function adds the two accumulator data item values: +<ul> +<li><pre> +<i>A</i> = <i>A</i> + <i>B</i> +<i>A</i> = <i>A</i> + <i>C</i> +// Same as +// <i>A</i> = (<i>A</i> + <i>B</i>) + <i>C</i> +</pre></li> +<li><pre> +<i>B</i> = <i>B</i> + <i>C</i> +<i>A</i> = <i>A</i> + <i>B</i> +// Same as +// <i>A</i> = <i>A</i> + (<i>B</i> + <i>C</i>) +// <i>B</i> = <i>B</i> + <i>C</i> +</li> +</ul> +Addition is associative, and so the combiner function is also. +</p> +</div> +<div class="note"> +<p><strong>Example:</strong> In the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, +<pre> +fMMCombiner(&<i>A</i>, &<i>B</i>) +</pre> +is the same as +<pre> +<i>A</i> = minmax(<i>A</i>, <i>B</i>) +</pre> +So the two sequences are +<ul> +<li><pre> +<i>A</i> = minmax(<i>A</i>, <i>B</i>) +<i>A</i> = minmax(<i>A</i>, <i>C</i>) +// Same as +// <i>A</i> = minmax(minmax(<i>A</i>, <i>B</i>), <i>C</i>) +</pre></li> +<li><pre> +<i>B</i> = minmax(<i>B</i>, <i>C</i>) +<i>A</i> = minmax(<i>A</i>, <i>B</i>) +// Same as +// <i>A</i> = minmax(<i>A</i>, minmax(<i>B</i>, <i>C</i>)) +// <i>B</i> = minmax(<i>B</i>, <i>C</i>) +</pre></li> +<code>minmax</code> is associative, and so <code>fMMCombiner</code> is also. +</p> +</div> + +<p><strong>The accumulator function and combiner function together must obey the <i>basic + folding rule</i>.</strong> That is, if <code><i>A</i></code> + and <code><i>B</i></code> are accumulator data items, <code><i>A</i></code> has been + initialized by the initializer function and may have been passed to the accumulator function + zero or more times, <code><i>B</i></code> has not been initialized, and <i>args</i> is + the list of input arguments and special arguments for a particular call to the accumulator + function, then the following two code sequences must set <code><i>A</i></code> + to <a href="#the-same">the same value</a>:</p> +<ul> +<li><pre> +<i>accumulatorName</i>(&<i>A</i>, <i>args</i>); // statement 1 +</pre></li> +<li><pre> +<i>initializerName</i>(&<i>B</i>); // statement 2 +<i>accumulatorName</i>(&<i>B</i>, <i>args</i>); // statement 3 +<i>combinerName</i>(&<i>A</i>, &<i>B</i>); // statement 4 +</pre></li> +</ul> +<div class="note"> +<p><strong>Example:</strong> In the <a href="#example-addint">addint</a> kernel, for an input value <i>V</i>: +<ul> +<li>Statement 1 is the same as <code>A += <i>V</i></code></li> +<li>Statement 2 is the same as <code>B = 0</code></li> +<li>Statement 3 is the same as <code>B += <i>V</i></code>, which is the same as <code>B = <i>V</i></code></li> +<li>Statement 4 is the same as <code>A += B</code>, which is the same as <code>A += <i>V</i></code></li> +</ul> +Statements 1 and 4 set <code><i>A</i></code> to the same value, and so this kernel obeys the +basic folding rule. +</p> +</div> +<div class="note"> +<p><strong>Example:</strong> In the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, for an input + value <i>V</i> at coordinate <i>X</i>: +<ul> +<li>Statement 1 is the same as <code>A = minmax(A, IndexedVal(<i>V</i>, <i>X</i>))</code></li> +<li>Statement 2 is the same as <code>B = <a href="#INITVAL">INITVAL</a></code></li> +<li>Statement 3 is the same as +<pre> +B = minmax(B, IndexedVal(<i>V</i>, <i>X</i>)) +</pre> +which, because <i>B</i> is the initial value, is the same as +<pre> +B = IndexedVal(<i>V</i>, <i>X</i>) +</pre> +</li> +<li>Statement 4 is the same as +<pre> +A = minmax(A, B) +</pre> +which is the same as +<pre> +A = minmax(A, IndexedVal(<i>V</i>, <i>X</i>)) +</pre> +</ul> +Statements 1 and 4 set <code><i>A</i></code> to the same value, and so this kernel obeys the +basic folding rule. +</p> +</div> + +<h3 id="calling-reduction-kernel">Calling a reduction kernel from Java code</h3> + +<p>For a reduction kernel named <i>kernelName</i> defined in the +file <code><i>filename</i>.rs</code>, there are three methods reflected in the +class <code>ScriptC_<i>filename</i></code>:</p> + +<pre> +// Method 1 +public <i>javaFutureType</i> reduce_<i>kernelName</i>(Allocation ain1, <i>…,</i> + Allocation ain<i>N</i>); + +// Method 2 +public <i>javaFutureType</i> reduce_<i>kernelName</i>(Allocation ain1, <i>…,</i> + Allocation ain<i>N</i>, + Script.LaunchOptions sc); + +// Method 3 +public <i>javaFutureType</i> reduce_<i>kernelName</i>(<i><a href="#devec">devecSiIn1Type</a></i>[] in1, …, + <i><a href="#devec">devecSiInNType</a></i>[] in<i>N</i>); +</pre> + +<p>Here are some examples of calling the <a href="#example-addint">addint</a> kernel:</p> +<pre> +ScriptC_example script = new ScriptC_example(mRenderScript); + +// 1D array +// and obtain answer immediately +int input1[] = <i>…</i>; +int sum1 = script.reduce_addint(input1).get(); // Method 3 + +// 2D allocation +// and do some additional work before obtaining answer +Type.Builder typeBuilder = + new Type.Builder(RS, Element.I32(RS)); +typeBuilder.setX(<i>…</i>); +typeBuilder.setY(<i>…</i>); +Allocation input2 = createTyped(RS, typeBuilder.create()); +<i>populateSomehow</i>(input2); // fill in input Allocation with data +script.result_int result2 = script.reduce_addint(input2); // Method 1 +<i>doSomeAdditionalWork</i>(); // might run at same time as reduction +int sum2 = result2.get(); +</pre> + +<p><strong>Method 1</strong> has one input {@link android.renderscript.Allocation} argument for + every input argument in the kernel's <a href="#accumulator-function">accumulator + function</a>. The RenderScript runtime checks to ensure that all of the input Allocations + have the same dimensions and that the {@link android.renderscript.Element} type of each of + the input Allocations matches that of the corresponding input argument of the accumulator + function's prototype. If any of these checks fail, RenderScript throws an exception. The + kernel executes over every coordinate in those dimensions.</p> + +<p><strong>Method 2</strong> is the same as Method 1 except that Method 2 takes an additional + argument <code>sc</code> that can be used to limit the kernel execution to a subset of the + coordinates.</p> + +<p><strong><a id="reduce-method-3">Method 3</a></strong> is the same as Method 1 except that + instead of taking Allocation inputs it takes Java array inputs. This is a convenience that + saves you from having to write code to explicitly create an Allocation and copy data to it + from a Java array. <em>However, using Method 3 instead of Method 1 does not increase the + performance of the code</em>. For each input array, Method 3 creates a temporary + 1-dimensional Allocation with the appropriate {@link android.renderscript.Element} type and + {@link android.renderscript.Allocation#setAutoPadding} enabled, and copies the array to the + Allocation as if by the appropriate <code>copyFrom()</code> method of {@link + android.renderscript.Allocation}. It then calls Method 1, passing those temporary + Allocations.</p> +<p class="note"><strong>NOTE:</strong> If your application will make multiple kernel calls with + the same array, or with different arrays of the same dimensions and Element type, you may improve + performance by explicitly creating, populating, and reusing Allocations yourself, instead of + by using Method 3.</p> +<p><strong><i><a id="javaFutureType">javaFutureType</a></i></strong>, + the return type of the reflected reduction methods, is a reflected + static nested class within the <code>ScriptC_<i>filename</i></code> + class. It represents the future result of a reduction + kernel run. To obtain the actual result of the run, call + the <code>get()</code> method of that class, which returns a value + of type <i>javaResultType</i>. <code>get()</code> is <a href="#asynchronous-model">synchronous</a>.</p> + +<pre> +public class ScriptC_<i>filename</i> extends ScriptC { + public static class <i>javaFutureType</i> { + public <i>javaResultType</i> get() { … } + } +} +</pre> + +<p><strong><i>javaResultType</i></strong> is determined from the <i>resultType</i> of the + <a href="#outconverter-function">outconverter function</a>. Unless <i>resultType</i> is an + unsigned type (scalar, vector, or array), <i>javaResultType</i> is the directly corresponding + Java type. If <i>resultType</i> is an unsigned type and there is a larger Java signed type, + then <i>javaResultType</i> is that larger Java signed type; otherwise, it is the directly + corresponding Java type. For example:</p> +<ul> +<li>If <i>resultType</i> is <code>int</code>, <code>int2</code>, or <code>int[15]</code>, + then <i>javaResultType</i> is <code>int</code>, <code>Int2</code>, + or <code>int[]</code>. All values of <i>resultType</i> can be represented + by <i>javaResultType</i>.</li> +<li>If <i>resultType</i> is <code>uint</code>, <code>uint2</code>, or <code>uint[15]</code>, + then <i>javaResultType</i> is <code>long</code>, <code>Long2</code>, + or <code>long[]</code>. All values of <i>resultType</i> can be represented + by <i>javaResultType</i>.</li> +<li>If <i>resultType</i> is <code>ulong</code>, <code>ulong2</code>, + or <code>ulong[15]</code>, then <i>javaResultType</i> + is <code>long</code>, <code>Long2</code>, or <code>long[]</code>. There are certain values + of <i>resultType</i> that cannot be represented by <i>javaResultType</i>.</li> +</ul> + +<p><strong><i>javaFutureType</i></strong> is the future result type corresponding + to the <i>resultType</i> of the <a href="#outconverter-function">outconverter + function</a>.</p> +<ul> +<li>If <i>resultType</i> is not an array type, then <i>javaFutureType</i> + is <code>result_<i>resultType</i></code>.</li> +<li>If <i>resultType</i> is an array of length <i>Count</i> with members of type <i>memberType</i>, + then <i>javaFutureType</i> is <code>resultArray<i>Count</i>_<i>memberType</i></code>.</li> +</ul> + +<p>For example:</p> + +<pre> +public class ScriptC_<i>filename</i> extends ScriptC { + // for kernels with int result + public static class result_int { + public int get() { … } + } + + // for kernels with int[10] result + public static class resultArray10_int { + public int[] get() { … } + } + + // for kernels with int2 result + // note that the Java type name "Int2" is not the same as the script type name "int2" + public static class result_int2 { + public Int2 get() { … } + } + + // for kernels with int2[10] result + // note that the Java type name "Int2" is not the same as the script type name "int2" + public static class resultArray10_int2 { + public Int2[] get() { … } + } + + // for kernels with uint result + // note that the Java type "long" is a wider signed type than the unsigned script type "uint" + public static class result_uint { + public long get() { … } + } + + // for kernels with uint[10] result + // note that the Java type "long" is a wider signed type than the unsigned script type "uint" + public static class resultArray10_uint { + public long[] get() { … } + } + + // for kernels with uint2 result + // note that the Java type "Long2" is a wider signed type than the unsigned script type "uint2" + public static class result_uint2 { + public Long2 get() { … } + } + + // for kernels with uint2[10] result + // note that the Java type "Long2" is a wider signed type than the unsigned script type "uint2" + public static class resultArray10_uint2 { + public Long2[] get() { … } + } +} +</pre> + +<p>If <i>javaResultType</i> is an object type (including an array type), each call + to <code><i>javaFutureType</i>.get()</code> on the same instance will return the same + object.</p> + +<p>If <i>javaResultType</i> cannot represent all values of type <i>resultType</i>, and a + reduction kernel produces an unrepresentible value, + then <code><i>javaFutureType</i>.get()</code> throws an exception.</p> + +<h4 id="devec">Method 3 and <i>devecSiInXType</i></h4> + +<p><strong><i>devecSiInXType</i></strong> is the Java type corresponding to + the <i>inXType</i> of the corresponding argument of + the <a href="#accumulator-function">accumulator function</a>. Unless <i>inXType</i> is an + unsigned type or a vector type, <i>devecSiInXType</i> is the directly corresponding Java + type. If <i>inXType</i> is an unsigned scalar type, then <i>devecSiInXType</i> is the + Java type directly corresponding to the signed scalar type of the same + size. If <i>inXType</i> is a signed vector type, then <i>devecSiInXType</i> is the Java + type directly corresponding to the vector component type. If <i>inXType</i> is an unsigned + vector type, then <i>devecSiInXType</i> is the Java type directly corresponding to the + signed scalar type of the same size as the vector component type. For example:</p> +<ul> +<li>If <i>inXType</i> is <code>int</code>, then <i>devecSiInXType</i> + is <code>int</code>.</li> +<li>If <i>inXType</i> is <code>int2</code>, then <i>devecSiInXType</i> + is <code>int</code>. The array is a <em>flattened</em> representation: It has twice as + many <em>scalar</em> Elements as the Allocation has 2-component <em>vector</em> + Elements. This is the same way that the <code>copyFrom()</code> methods of {@link + android.renderscript.Allocation} work.</li> +<li>If <i>inXType</i> is <code>uint</code>, then <i>deviceSiInXType</i> + is <code>int</code>. A signed value in the Java array is interpreted as an unsigned value of + the same bitpattern in the Allocation. This is the same way that the <code>copyFrom()</code> + methods of {@link android.renderscript.Allocation} work.</li> +<li>If <i>inXType</i> is <code>uint2</code>, then <i>deviceSiInXType</i> + is <code>int</code>. This is a combination of the way <code>int2</code> and <code>uint</code> + are handled: The array is a flattened representation, and Java array signed values are + interpreted as RenderScript unsigned Element values.</li> +</ul> + +<p>Note that for <a href="#reduce-method-3">Method 3</a>, input types are handled differently +than result types:</p> + +<ul> +<li>A script's vector input is flattened on the Java side, whereas a script's vector result is not.</li> +<li>A script's unsigned input is represented as a signed input of the same size on the Java + side, whereas a script's unsigned result is represented as a widened signed type on the Java + side (except in the case of <code>ulong</code>).</li> +</ul> + +<h3 id="more-example">More example reduction kernels</h3> + +<pre id="dot-product"> +#pragma rs reduce(dotProduct) \ + accumulator(dotProductAccum) combiner(dotProductSum) + +// Note: No initializer function -- therefore, +// each accumulator data item is implicitly initialized to 0.0f. + +static void dotProductAccum(float *accum, float in1, float in2) { + *accum += in1*in2; +} + +// combiner function +static void dotProductSum(float *accum, const float *val) { + *accum += *val; +} +</pre> + +<pre> +// Find a zero Element in a 2D allocation; return (-1, -1) if none +#pragma rs reduce(fz2) \ + initializer(fz2Init) \ + accumulator(fz2Accum) combiner(fz2Combine) + +static void fz2Init(int2 *accum) { accum->x = accum->y = -1; } + +static void fz2Accum(int2 *accum, + int inVal, + int x /* special arg */, + int y /* special arg */) { + if (inVal==0) { + accum->x = x; + accum->y = y; + } +} + +static void fz2Combine(int2 *accum, const int2 *accum2) { + if (accum2->x >= 0) *accum = *accum2; +} +</pre> + +<pre> +// Note that this kernel returns an array to Java +#pragma rs reduce(histogram) \ + accumulator(hsgAccum) combiner(hsgCombine) + +#define BUCKETS 256 +typedef uint32_t Histogram[BUCKETS]; + +// Note: No initializer function -- +// therefore, each bucket is implicitly initialized to 0. + +static void hsgAccum(Histogram *h, uchar in) { ++(*h)[in]; } + +static void hsgCombine(Histogram *accum, + const Histogram *addend) { + for (int i = 0; i < BUCKETS; ++i) + (*accum)[i] += (*addend)[i]; +} + +// Determines the mode (most frequently occurring value), and returns +// the value and the frequency. +// +// If multiple values have the same highest frequency, returns the lowest +// of those values. +// +// Shares functions with the histogram reduction kernel. +#pragma rs reduce(mode) \ + accumulator(hsgAccum) combiner(hsgCombine) \ + outconverter(modeOutConvert) + +static void modeOutConvert(int2 *result, const Histogram *h) { + uint32_t mode = 0; + for (int i = 1; i < BUCKETS; ++i) + if ((*h)[i] > (*h)[mode]) mode = i; + result->x = mode; + result->y = (*h)[mode]; +} +</pre> diff --git a/docs/html/topic/libraries/support-library/features.jd b/docs/html/topic/libraries/support-library/features.jd index 0f63bf669687..614392e00d44 100755 --- a/docs/html/topic/libraries/support-library/features.jd +++ b/docs/html/topic/libraries/support-library/features.jd @@ -7,7 +7,15 @@ page.title=Support Library Features <h2>In this document</h2> <ol> - <li><a href="#v4">v4 Support Library</a></li> + <li><a href="#v4">v4 Support Libraries</a> + <ol> + <li><a href="#v4-compat">v4 compat library</a></li> + <li><a href="#v4-core-utils">v4 core-utils library</a></li> + <li><a href="#v4-core-ui">v4 core-ui library</a></li> + <li><a href="#v4-media-compat">v4 media-compat library</a></li> + <li><a href="#v4-fragment">v4 fragment library</a></li> + </ol> + </li> <li><a href="#multidex">Multidex Support Library</a></li> <li><a href="#v7">v7 Support Libraries</a> <ol> @@ -63,94 +71,115 @@ page.title=Support Library Features include the library in your application.</p> -<h2 id="v4">v4 Support Library</h2> - -<p>This library is designed to be used with Android 1.6 (API level 4) and higher. It includes the - largest set of APIs compared to the other libraries, including support for application components, - user interface features, accessibility, data handling, network connectivity, and programming - utilities. Here are a few of the key classes included in the v4 library:</p> +<h2 id="v4">v4 Support Libraries</h2> -<ul> - <li>App Components - <ul> - <li>{@link android.support.v4.app.Fragment} - - Adds support for encapsulation of user interface and functionality - with Fragments, enabling - applications to provide layouts that adjust between small and - large-screen devices. - </li> - - <li>{@link android.support.v4.app.NotificationCompat} - Adds support for rich notification - features.</li> - <li>{@link android.support.v4.content.LocalBroadcastManager} - Allows applications to easily - register for and receive intents within a single application without broadcasting them - globally.</li> - </ul> - </li> - <li>User Interface - <ul> - <li>{@link android.support.v4.view.ViewPager} - Adds a - {@link android.view.ViewGroup} that manages the layout for the - child views, which the user can swipe between.</li> - <li>{@link android.support.v4.view.PagerTitleStrip} - - Adds a non-interactive title strip, that can be added as a child of - {@link android.support.v4.view.ViewPager}.</li> - <li>{@link android.support.v4.view.PagerTabStrip} - Adds a - navigation widget for switching between paged views, that can also be used with - {@link android.support.v4.view.ViewPager}.</li> - <li>{@link android.support.v4.widget.DrawerLayout} - Adds - support for creating a <a href="{@docRoot}training/implementing-navigation/nav-drawer.html" - >Navigation Drawer</a> that can be pulled in from the edge of a window.</li> - <li>{@link android.support.v4.widget.SlidingPaneLayout} - - Adds widget for creating linked summary and detail views that - appropriately adapt to various screen sizes.</li> - </ul> - </li> - <li>Accessibility - <ul> - <li>{@link android.support.v4.widget.ExploreByTouchHelper} - - Adds a helper class for implementing accessibility support for custom views.</li> - <li>{@link android.support.v4.view.accessibility.AccessibilityEventCompat} - Adds support for - {@link android.view.accessibility.AccessibilityEvent}. For more information about implementing - accessibility, see <a href="{@docRoot}guide/topics/ui/accessibility/index.html" - >Accessibility</a>.</li> - - <li>{@link android.support.v4.view.accessibility.AccessibilityNodeInfoCompat} - Adds support - for {@link android.view.accessibility.AccessibilityNodeInfo}.</li> - <li>{@link android.support.v4.view.accessibility.AccessibilityNodeProviderCompat} - Adds - support for {@link android.view.accessibility.AccessibilityNodeProvider}.</li> - <li>{@link android.support.v4.view.AccessibilityDelegateCompat} - Adds support for - {@link android.view.View.AccessibilityDelegate}.</li> - </ul> - </li> - <li>Content - <ul> - <li>{@link android.support.v4.content.Loader} - Adds support for asynchronous loading of data. - The library also provides concrete implementations of this class, including - {@link android.support.v4.content.CursorLoader} and - {@link android.support.v4.content.AsyncTaskLoader}.</li> - <li>{@link android.support.v4.content.FileProvider} - Adds support for sharing of private - files between applications.</li> - </ul> - </li> -</ul> +<p> + These libraries are designed to be used with Android 2.3 (API level 9) and + higher. They include the largest set of APIs compared to the other libraries, + including support for application components, user interface features, + accessibility, data handling, network connectivity, and programming + utilities. +</p> <p> - There are many other APIs included in this library. For complete, detailed information about the - v4 Support Library APIs, see the {@link android.support.v4.app android.support.v4} package in the - API reference. + For complete, detailed information about the classes and methods provided by + the v4 support libraries, see the {@link android.support.v4.app + android.support.v4} package in the API reference. +</p> + + +<p class="note"> + <strong>Note:</strong> Prior to Support Library revision 24.2.0, there was a + single v4 support library. That library was divided into multiple modules to + improve efficiency. For backwards compatibility, if you list + <code>support-v4</code> in your Gradle script, your APK will include all of + the v4 modules. However, to reduce APK size, we recommend that you just list + the specific modules your app needs. </p> -<p class="caution"><strong>Caution:</strong> Using dynamic dependencies, especially for higher version -numbers, can cause unexpected version updates and regression incompatibilities.</p> +<h3 id="v4-compat">v4 compat library</h3> + +<p> + Provides compatibility wrappers for a number of framework APIs, such as + <code>Context.obtainDrawable()</code> and + <code>View.performAccessibilityAction()</code>. +</p> <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:support-v4:24.1.1 +com.android.support:support-compat:24.2.0 </pre> +<h3 id="v4-core-utils">v4 core-utils library</h3> + +<p> + Provides a number of utility classes, such as {@link + android.support.v4.content.AsyncTaskLoader} and {@link + android.support.v4.content.PermissionChecker}. +</p> +<p> + The Gradle build script dependency identifier for this library is as follows: +</p> + +<pre> +com.android.support:support-core-utils:24.2.0 +</pre> + +<h3 id="v4-core-ui">v4 core-ui library</h3> + +<p> + Implements a variety of UI-related components, such as {@link + android.support.v4.view.ViewPager}, {@link + android.support.v4.widget.NestedScrollView}, and {@link + android.support.v4.widget.ExploreByTouchHelper}. +</p> + +<p> + The Gradle build script dependency identifier for this library is as follows: +</p> + +<pre> +com.android.support:support-core-ui:24.2.0 +</pre> + +<h3 id="v4-media-compat">v4 media-compat library</h3> + +<p> + Backports portions of the <a href= + "/reference/android/media/package-summary.html">media</a> framework, + including {@link android.media.browse.MediaBrowser} and {@link + android.media.session.MediaSession}. +</p> + +<p> + The Gradle build script dependency identifier for this library is as follows: +</p> + +<pre> +com.android.support:support-media-compat:24.2.0 +</pre> + +<h3 id="v4-fragment">v4 fragment library</h3> + +<p> + Adds support for encapsulation of user interface and functionality with + <a href= + "/guide/components/fragments.html">fragments</a>, + enabling applications to provide layouts that adjust between small and + large-screen devices. This module has dependencies on <a href= + "#v4-compat">compat</a>, <a href="#v4-core-utils">core-utils</a>, <a href= + "#v4-core-ui">core-ui</a>, and <a href="#v4-media-compat">media-compat</a>. +</p> + +<p> + The Gradle build script dependency identifier for this library is as follows: +</p> + +<pre> +com.android.support:support-fragment:24.2.0 +</pre> <h2 id="multidex">Multidex Support Library</h2> @@ -173,7 +202,7 @@ com.android.support:multidex:1.0.0 <h2 id="v7">v7 Support Libraries</h2> -<p>There are several libraries designed to be used with Android 2.1 (API level 7) and higher. +<p>There are several libraries designed to be used with Android 2.3 (API level 9) and higher. These libraries provide specific feature sets and can be included in your application independently from each other.</p> @@ -216,7 +245,7 @@ com.android.support:multidex:1.0.0 <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:appcompat-v7:24.1.1 +com.android.support:appcompat-v7:24.2.0 </pre> @@ -231,7 +260,7 @@ implementations, and are used extensively in layouts for TV apps.</p> <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:cardview-v7:24.1.1 +com.android.support:cardview-v7:24.2.0 </pre> @@ -247,7 +276,7 @@ For detailed information about the v7 gridlayout library APIs, see the <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:gridlayout-v7:24.1.1 +com.android.support:gridlayout-v7:24.2.0 </pre> @@ -270,7 +299,7 @@ reference.</p> <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:mediarouter-v7:24.1.1 +com.android.support:mediarouter-v7:24.2.0 </pre> <p class="caution">The v7 mediarouter library APIs introduced in Support Library @@ -290,7 +319,7 @@ title card.</p> <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:palette-v7:24.1.1 +com.android.support:palette-v7:24.2.0 </pre> @@ -306,7 +335,7 @@ limited window of data items.</p> <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:recyclerview-v7:24.1.1 +com.android.support:recyclerview-v7:24.2.0 </pre> @@ -329,18 +358,18 @@ such as {@link android.support.v7.preference.CheckBoxPreference} and <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:preference-v7:24.1.1 +com.android.support:preference-v7:24.2.0 </pre> <h2 id="v8">v8 Support Library</h2> -<p>This library is designed to be used with Android 2.2 (API level 8) and higher. +<p>This library is designed to be used with Android 2.3 (API level 9) and higher. This library provides specific feature sets and can be included in your application independently from other libraries.</p> <h3 id="v8-renderscript">v8 renderscript library</h3> -<p>This library is designed to be used with Android (API level 8) and higher. It adds support for +<p>This library is designed to be used with Android 2.3 (API level 9) and higher. It adds support for the <a href="{@docRoot}guide/topics/renderscript/compute.html">RenderScript</a> computation framework. These APIs are included in the {@link android.support.v8.renderscript} package. You should be aware that the steps for including these APIs in your application is <em>very @@ -380,7 +409,7 @@ defaultConfig { <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:support-v13:24.1.1 +com.android.support:support-v13:24.2.0 </pre> @@ -406,7 +435,7 @@ for preference interfaces such as <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:preference-v14:24.1.1 +com.android.support:preference-v14:24.2.0 </pre> @@ -429,7 +458,7 @@ interface and classes, such as <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:preference-leanback-v17:24.1.1 +com.android.support:preference-leanback-v17:24.2.0 </pre> @@ -465,7 +494,7 @@ com.android.support:preference-leanback-v17:24.1.1 <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:leanback-v17:24.1.1 +com.android.support:leanback-v17:24.2.0 </pre> @@ -480,7 +509,7 @@ package provides APIs to support adding annotation metadata to your apps. </p> <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:support-annotations:24.1.1 +com.android.support:support-annotations:24.2.0 </pre> @@ -498,7 +527,7 @@ snackbars, and <a href="{@docRoot}design/building-blocks/tabs.html">tabs</a>. < <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:design:24.1.1 +com.android.support:design:24.2.0 </pre> @@ -519,7 +548,7 @@ Callback</a>. </p> <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:customtabs:24.1.1 +com.android.support:customtabs:24.2.0 </pre> @@ -543,7 +572,7 @@ PercentRelativeLayout</a>. </p> <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:percent:24.1.1 +com.android.support:percent:24.2.0 </pre> @@ -566,5 +595,5 @@ RecommendationExtender</a>. </p> <p>The Gradle build script dependency identifier for this library is as follows:</p> <pre> -com.android.support:recommendation:24.1.1 +com.android.support:recommendation:24.2.0 </pre> diff --git a/docs/html/topic/libraries/support-library/revisions.jd b/docs/html/topic/libraries/support-library/revisions.jd index 3b25fb020767..4e14c7054bbd 100644 --- a/docs/html/topic/libraries/support-library/revisions.jd +++ b/docs/html/topic/libraries/support-library/revisions.jd @@ -6,9 +6,324 @@ page.metaDescription=This page provides details about the Support Library packag <p>This page provides details about the Support Library package releases.</p> <div class="toggle-content opened"> - <p id="rev24-1-1"> + <p id="rev24-2-0"> <a href="#" onclick="return toggleContent(this)"><img src= "{@docRoot}assets/images/styles/disclosure_up.png" class= + "toggle-content-img" alt="">Android Support Library, revision 24.2.0</a> + <em>(August 2016)</em> + </p> + + <div class="toggle-content-toggleme"> + +<p>Release 24.2.0 contains the following changes:</p> + +<ul> + <li><a href="#24-2-0-v4-refactor">v4 Support Library split</a></li> + <li><a href="#24-2-0-api-updates">API updates</a></li> + <li><a href="#24-2-0-behavior">Behavior changes</a></li> + <li><a href="#24-2-0-deprecations">Deprecations</a></li> + <li><a href="#24-2-0-bugfixes">Bug fixes</a></li> +</ul> + +<p class="note"><strong>Note:</strong> Release 24.2.0 removes support for + Android 2.2 (API level 8) and lower. Classes and methods that exist only to + serve those system versions are now marked as deprecated and should no longer + be used. These deprecated classes and methods may be removed in a future + release. +</p> + +<h3 id="24-2-0-v4-refactor">v4 Support Library split</h3> + +<p>With this release, the <a href="features.html#v4">v4 Support Library</a> has + been split into several smaller modules:</p> + +<dl> + <dt> + <code>support-compat</code> + </dt> + + <dd> + Provides compatibility wrappers for new framework APIs, such as + <code>Context.getDrawable()</code> and + <code>View.performAccessibilityAction()</code>. + </dd> + + <dt> + <code>support-core-utils</code> + </dt> + + <dd> + Provides a number of utility classes, such as {@link + android.support.v4.content.AsyncTaskLoader} and {@link + android.support.v4.content.PermissionChecker}. + </dd> + + <dt> + <code>support-core-ui</code> + </dt> + + <dd> + Implements a variety of UI-related components, such as {@link + android.support.v4.view.ViewPager}, {@link + android.support.v4.widget.NestedScrollView}, and {@link + android.support.v4.widget.ExploreByTouchHelper}. + </dd> + + <dt> + <code>support-media-compat</code> + </dt> + + <dd> + Backports portions of the <a href= + "/reference/android/media/package-summary.html">media</a> framework, + including {@link android.media.browse.MediaBrowser} and {@link + android.media.session.MediaSession}. + </dd> + + <dt> + <code>support-fragment</code> + </dt> + + <dd> + Backports the <a href= + "/guide/components/fragments.html">fragment</a> + framework. This module has dependencies on <code>support-compat</code>, + <code>support-core-utils</code>, <code>support-core-ui</code>, and + <code>support-media-compat</code>. + </dd> +</dl> + +<p>For backwards compatibility, if you list <code>support-v4</code> in your +Gradle script, your APK will include all of these modules. However, to reduce +APK size, we recommend that you just list the specific modules your app needs. +</p> + +<h3 id="24-2-0-api-updates">API updates</h3> + +<ul> + <li>Clients using <a href="features.html#custom-tabs">Custom Tabs</a> can + control whether Instant Apps should open. (Note that Instant Apps are not yet + generally available.) To enable or disable Instant Apps, call <a href= + "/reference/android/support/customtabs/CustomTabsIntent.Builder.html#setInstantAppsEnabled(boolean)"> + <code>CustomTabsIntent.Builder.setInstantAppsEnabled()</code></a> or + specify <a href= + "/reference/android/support/customtabs/CustomTabsIntent.html#EXTRA_ENABLE_INSTANT_APPS"> + <code>EXTRA_ENABLE_INSTANT_APPS</code></a>. By default, Custom Tabs will + default to enabling Instant Apps, when that feature becomes available. + </li> + + <li>{@link android.support.design.widget.TextInputLayout} adds support for + the <a href= + "https://material.google.com/components/text-fields.html#text-fields-password-input"> + password visibility toggle</a> from the material design specification. + </li> + + <li>The new <a href= + "/reference/android/support/transition/package-summary.html" + ><code>android.support.transition</code></a> + package backports the <a href= + "/training/transitions/index.html">Transitions</a> framework to API levels 14 + and higher. For more information, see the <a href= + "/reference/android/support/transition/package-summary.html" + ><code>android.support.transition</code></a> reference. + </li> + + <li>The <a href="features.html#custom-tabs">Custom Tabs support library</a> + adds support for using {@link android.widget.RemoteViews} in the secondary + toolbar. The existing {@link + android.support.customtabs.CustomTabsSession#setToolbarItem setToolbarItem()} + method is now deprecated. + </li> + + <li>{@link android.support.v7.content.res.AppCompatResources} adds the + ability to load a <code><vector></code> (on API level 9 and higher) or + <code><animated-vector></code> (on API level 11 and higher) from a + resource ID, by using the new <a href= + "/reference/android/support/v7/content/res/AppCompatResources.html#getDrawable(android.content.Context,%20int)" + ><code>getDrawable()</code></a> method. + </li> + + <li>{@link android.support.design.widget.CoordinatorLayout} now supports + defining inset views, and specifying that other views should dodge the inset + views. This allows apps to replicate behavior patterns similar to the way + {@link android.support.design.widget.FloatingActionButton} moves out of the + way of a {@link android.support.design.widget.Snackbar}, but for any + arbitrary view children. For more information, see the <a href= + "/reference/android/support/design/widget/CoordinatorLayout.LayoutParams.html#insetEdge"> + <code>LayoutParams.insetEdge</code></a> and <a href= + "/reference/android/support/design/widget/CoordinatorLayout.LayoutParams.html#dodgeInsetEdges"> + <code>LayoutParams.dodgeInsetEdges</code></a> reference documentation. + </li> + + <li>The new <a href="/reference/android/support/v7/util/DiffUtil.html"><code> + DiffUtil</code></a> class can calculate the difference between two + collections, and can dispatch a list of update operations that are suitable + to be consumed by a {@link android.support.v7.widget.RecyclerView.Adapter}. + </li> + + <li> + <a href= + "/reference/android/support/v7/widget/RecyclerView.OnFlingListener.html"><code> + RecyclerView.OnFlingListener</code></a> has been added to support custom + behavior in response to flings. The <a href= + "/reference/android/support/v7/widget/SnapHelper.html"><code>SnapHelper</code></a> + class provides an implementation specifically for snapping child views, and + the <a href= + "/reference/android/support/v7/widget/LinearSnapHelper.html"><code>LinearSnapHelper</code></a> + class extends this implementation to provide center-aligned snapping + behavior similar to {@link android.support.v4.view.ViewPager}. + </li> + +</ul> + +<h3 id="24-2-0-behavior">Behavior changes</h3> + +<ul> + <li>If you use the appcompat library's day/night functionality, the system + now automatically recreates your activity whenever the day/night mode changes + (either because of the time of day, or because of a call to {@link + android.support.v7.app.AppCompatDelegate#setLocalNightMode + AppCompatDelegate.setLocalNightMode()}). + </li> + + <li>{@link android.support.design.widget.Snackbar} now draws behind the + navigation bar if the status bar is translucent. + </li> +</ul> + +<h3 id="24-2-0-deprecations">Deprecations</h3> + +<p>Deprecated classes and methods are subject to removal in a future release. You should migrate away from these APIs as soon as possible.</p> + +<ul> + <li>Several methods on the following classes were only required for API 8 and + lower, and should no longer be used. Instead, use the framework + implementations. + <ul> + <li>{@link android.support.v4.view.KeyEventCompat}: Replace with {@link + android.view.KeyEvent} + </li> + + <li>{@link android.support.v4.view.MotionEventCompat}: Use {@link + android.view.MotionEvent} + </li> + + <li>{@link android.support.v4.view.ViewCompat}: Use {@link + android.view.View} + </li> + + <li>{@link android.support.v4.view.ViewConfigurationCompat}: Use {@link + android.view.ViewConfiguration} + </li> + </ul> + </li> + + <li> + {@link android.support.v4.accessibilityservice.AccessibilityServiceInfoCompat#getDescription + AccessibilityServiceInfoCompat.getDescription()} + has been deprecated in favor of + <a href="/reference/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.html#loadDescription(android.accessibilityservice.AccessibilityServiceInfo, android.content.pm.PackageManager)"><code>loadDescription()</code></a> + which returns a correctly localized description. + </li> + + <li>You should not instantiate the <code>ActivityCompat</code> class + directly. The non-static <code>getReferrer(Activity)</code> method will be + made static in an upcoming release. + </li> + + <li>{@link android.support.design.widget.CoordinatorLayout.Behavior#isDirty + CoordinatorLayout.Behavior.isDirty()} has been deprecated and is no longer + called by {@link android.support.design.widget.CoordinatorLayout}. Any + implementations, as well as any calls to this method, should be removed. + </li> + + <li>{@link android.support.v4.media.session.MediaSessionCompat#obtain + MediaSessionCompat.obtain()} has been deprecated and replaced with the more + appropriately-named method + <a href="/reference/android/support/v4/media/session/MediaSessionCompat.html#fromMediaSession"><code>fromMediaSession()</code></a>. + </li> + + <li>{@link + android.support.v4.media.session.MediaSessionCompat.QueueItem#obtain + MediaSessionCompat.QueueItem.obtain()} has been deprecated and replaced with + the more appropriately-named method + <a href="/reference/android/support/v4/media/session/MediaSessionCompat.QueueItem.html#fromQueueItem"><code>fromQueueItem()</code></a>. + </li> + + <li>Several abstract classes have been deprecated and replaced with + interfaces that more closely reflect their framework equivalents. + <ul> + <li>{@link + android.support.v4.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat} + has been replaced by the <a href= + "/reference/android/support/v4/view/accessibility/AccessibilityManagerCompat.AccessibilityStateChangeListener.html"> + <code>AccessibilityManagerCompat.AccessibilityStateChangeListener</code></a> + interface. + </li> + + <li>{@link + android.support.v4.widget.SearchViewCompat.OnCloseListenerCompat} has + been replaced by the <a + href="/reference/android/support/v4/widget/SearchViewCompat.OnCloseListener.html" + ><code>SearchViewCompat.OnCloseListener</code></a> interface. + </li> + + <li>{@link + android.support.v4.widget.SearchViewCompat.OnQueryTextListenerCompat } + has been replaced by the <a + href="/reference/android/support/v4/widget/SearchViewCompat.OnQueryTextListener.html" + ><code>SearchViewCompat.OnQueryTextListener</code></a> + interface. + </li> + </ul> + </li> + + <li>{@link android.support.customtabs.CustomTabsSession#setToolbarItem + CustomTabsSession.setToolbarItem()} has been deprecated and replaced by the + RemoteViews-based <a href= + "/reference/android/support/customtabs/CustomTabsSession.html#setSecondaryToolbarViews"> + <code>setSecondaryToolbarViews()</code></a>. + </li> +</ul> + +<h3 id="24-2-0-bugfixes">Bug fixes</h3> + +<p>The following known issues have been fixed with release 24.2.0:</p> + +<ul> + <li>Ensure <code>SwipeRefreshLayout</code> indicator is shown when + <code>setRefreshing(true)</code> is called before the first measurement pass + (<a href="https://code.google.com/p/android/issues/detail?id=77712">AOSP + issue 77712</a>) + </li> + + <li>Prevent <code>TabLayout</code> from flickering when changing pages + (<a href="https://code.google.com/p/android/issues/detail?id=180454">AOSP + issue 180454</a>) + </li> + + <li>Avoid <code>ClassNotFoundException</code> when unmarshalling + <code>SavedState</code> on API level 11 and lower (<a href= + "https://code.google.com/p/android/issues/detail?id=196430">AOSP issue + 196430</a>) + </li> +</ul> + +<p> + A complete list of public bug fixes is available on the <a href= + "https://code.google.com/p/android/issues/list?can=1&q=Component%3DSupport-Libraries+Target%3DSupport-24.2.0"> + AOSP Issue Tracker</a>. +</p> + + </div> +</div> + +<!-- end of collapsible section: 24.2.0 --> + +<div class="toggle-content closed"> + <p id="rev24-1-1"> + <a href="#" onclick="return toggleContent(this)"><img src= + "{@docRoot}assets/images/styles/disclosure_down.png" class= "toggle-content-img" alt="">Android Support Library, revision 24.1.1</a> <em>(July 2016)</em> </p> @@ -76,7 +391,7 @@ page.metaDescription=This page provides details about the Support Library packag <ul> <li>TabLayout.setCustomView(null) results in NullPointerException (<a href="https://code.google.com/p/android/issues/detail?id=214753">AOSP - issue</a>) + issue 214753</a>) </li> <li>TabLayout incorrectly highlights custom tabs (<a href= diff --git a/docs/html/topic/libraries/support-library/setup.jd b/docs/html/topic/libraries/support-library/setup.jd index 0cb9389ff508..adb263cbcd9a 100755 --- a/docs/html/topic/libraries/support-library/setup.jd +++ b/docs/html/topic/libraries/support-library/setup.jd @@ -85,17 +85,24 @@ Android Support Repository selected.</p> <li>Make sure you have downloaded the <strong>Android Support Repository</strong> using the <a href="#download">SDK Manager</a>.</li> <li>Open the {@code build.gradle} file for your application.</li> - <li>Add the support library to the {@code dependencies} section. For example, to add the v4 - support library, add the following lines: + <li>Add the support library to the {@code dependencies} section. For + example, to add the v4 core-utils library, add the following lines: <pre> dependencies { ... - <b>compile "com.android.support:support-v4:24.1.1"</b> + <b>compile "com.android.support:support-core-utils:24.2.0"</b> } </pre> </li> </ol> +<p class="caution"> + <strong>Caution:</strong> Using dynamic dependencies (for example, + <code>palette-v7:23.0.+</code>) can cause unexpected version updates and + regression incompatibilities. We recommend that you explicitly specify a + library version (for example, <code>palette-v7:24.2.0</code>). +</p> + <h2 id="using-apis">Using Support Library APIs</h2> <p>Support Library classes that provide support for existing framework APIs typically have the @@ -141,12 +148,12 @@ dependencies { <pre> <uses-sdk - android:minSdkVersion="<b>7</b>" - android:targetSdkVersion="17" /> + android:minSdkVersion="<b>14</b>" + android:targetSdkVersion="23" /> </pre> <p>The manifest setting tells Google Play that your application can be installed on devices with Android - 2.1 (API level 7) and higher. </p> + 4.0 (API level 14) and higher. </p> <p>If you are using Gradle build files, the <code>minSdkVersion</code> setting in the build file overrides the manifest settings. </p> @@ -158,7 +165,7 @@ android { ... defaultConfig { - minSdkVersion 8 + minSdkVersion 16 ... } ... @@ -166,13 +173,15 @@ android { </pre> <p>In this case, the build file setting tells Google Play that the default build variant of your - application can be installed on devices with Android 2.2 (API level 8) and higher. For more + application can be installed on devices with Android 4.1 (API level 16) and higher. For more information about build variants, see <a href="{@docRoot}studio/build/index.html">Build System Overview</a>. </p> <p class="note"> - <strong>Note:</strong> If you are including the v4 support and v7 appcompat libraries in your - application, you should specify a minimum SDK version of <code>"7"</code> (and not - <code>"4"</code>). The highest support library level you include in your application determines - the lowest API version in which it can operate. + <strong>Note:</strong> If you are including several support libraries, the + minimum SDK version must be the <em>highest</em> version required by any of + the specified libraries. For example, if your app includes both the <a href= + "features.html#v14-preference">v14 Preference Support library</a> and the + <a href="features.html#v17-leanback">v17 Leanback library</a>, your minimum + SDK version must be 17 or higher. </p> diff --git a/docs/html/training/implementing-navigation/nav-drawer.jd b/docs/html/training/implementing-navigation/nav-drawer.jd index d359a47e6413..abc79b649926 100644 --- a/docs/html/training/implementing-navigation/nav-drawer.jd +++ b/docs/html/training/implementing-navigation/nav-drawer.jd @@ -173,7 +173,7 @@ android.app.Fragment} into the main content view (the <pre> private class DrawerItemClickListener implements ListView.OnItemClickListener { @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { selectItem(position); } } diff --git a/docs/html/training/notify-user/navigation.jd b/docs/html/training/notify-user/navigation.jd index 65f8d48ba375..d0ec1cddc35c 100644 --- a/docs/html/training/notify-user/navigation.jd +++ b/docs/html/training/notify-user/navigation.jd @@ -205,7 +205,7 @@ Intent notifyIntent = notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); // Creates the PendingIntent -PendingIntent notifyIntent = +PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, @@ -214,7 +214,7 @@ PendingIntent notifyIntent = ); // Puts the PendingIntent into the notification builder -builder.setContentIntent(notifyIntent); +builder.setContentIntent(pendingIntent); // Notifications are issued by sending them to the // NotificationManager system service. NotificationManager mNotificationManager = diff --git a/docs/html/training/transitions/index.jd b/docs/html/training/transitions/index.jd index 53faa01aad8b..b8f99c84fc93 100644 --- a/docs/html/training/transitions/index.jd +++ b/docs/html/training/transitions/index.jd @@ -48,11 +48,9 @@ animate changes between view hierarchies. This class also covers how to create c animations.</p> <p class="note"><strong>Note:</strong> For Android versions earlier than 4.4.2 (API level 19) -but greater than or equal to Android 4.0 (API level 14), use the <code>animateLayoutChanges</code> -attribute to animate layouts. To learn more, see -<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a> and -<a href="{@docRoot}training/animation/layout.html">Animating Layout Changes</a>.</p> - +but greater than or equal to Android 4.0 (API level 14), use the Android Support +Library's <a href="/reference/android/support/transitions/package-summary.html" +><code>android.support.transition</code></a> package.</p> <h2>Lessons</h2> diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index e10445adfe29..0f305f3cff3d 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -293,10 +293,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { // to UI thread animation for AVD. if (!mAnimatorSet.isRunning() && ((VectorDrawableAnimatorRT) mAnimatorSet).mPendingAnimationActions.size() > 0) { - VectorDrawableAnimatorRT oldAnim = (VectorDrawableAnimatorRT) mAnimatorSet; - mAnimatorSet = new VectorDrawableAnimatorUI(this); - mAnimatorSet.init(mAnimatorSetFromXml); - oldAnim.transferPendingActions(mAnimatorSet); + fallbackOntoUI(); } } mAnimatorSet.onDraw(canvas); @@ -539,10 +536,22 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { throw new UnsupportedOperationException("Cannot force Animated Vector Drawable to" + " run on UI thread when the animation has started on RenderThread."); } + fallbackOntoUI(); + } + } + + private void fallbackOntoUI() { + if (mAnimatorSet instanceof VectorDrawableAnimatorRT) { + VectorDrawableAnimatorRT oldAnim = (VectorDrawableAnimatorRT) mAnimatorSet; mAnimatorSet = new VectorDrawableAnimatorUI(this); if (mAnimatorSetFromXml != null) { mAnimatorSet.init(mAnimatorSetFromXml); } + // Transfer the listener from RT animator to UI animator + if (oldAnim.mListener != null) { + mAnimatorSet.setListener(oldAnim.mListener); + } + oldAnim.transferPendingActions(mAnimatorSet); } } diff --git a/libs/common_time/common_clock_service.h b/libs/common_time/common_clock_service.h index bd663f06d43a..aea507ec0f30 100644 --- a/libs/common_time/common_clock_service.h +++ b/libs/common_time/common_clock_service.h @@ -53,7 +53,7 @@ class CommonClockService : public BnCommonClock, void notifyOnTimelineChanged(uint64_t timelineID); private: - CommonClockService(CommonTimeServer& timeServer) + explicit CommonClockService(CommonTimeServer& timeServer) : mTimeServer(timeServer) { }; virtual void binderDied(const wp<IBinder>& who); diff --git a/libs/common_time/common_time_config_service.h b/libs/common_time/common_time_config_service.h index 89806dd87e29..23abb1a9e47e 100644 --- a/libs/common_time/common_time_config_service.h +++ b/libs/common_time/common_time_config_service.h @@ -49,7 +49,7 @@ class CommonTimeConfigService : public BnCommonTimeConfig { virtual status_t forceNetworklessMasterMode(); private: - CommonTimeConfigService(CommonTimeServer& timeServer) + explicit CommonTimeConfigService(CommonTimeServer& timeServer) : mTimeServer(timeServer) { } CommonTimeServer& mTimeServer; diff --git a/libs/hwui/debug/gles_redefine.h b/libs/hwui/debug/gles_redefine.h new file mode 100644 index 000000000000..201f0a945209 --- /dev/null +++ b/libs/hwui/debug/gles_redefine.h @@ -0,0 +1,913 @@ +/* + * 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 glActiveShaderProgram wrap_glActiveShaderProgram +#define glActiveShaderProgramEXT wrap_glActiveShaderProgramEXT +#define glActiveTexture wrap_glActiveTexture +#define glAlphaFunc wrap_glAlphaFunc +#define glAlphaFuncQCOM wrap_glAlphaFuncQCOM +#define glAlphaFuncx wrap_glAlphaFuncx +#define glAlphaFuncxOES wrap_glAlphaFuncxOES +#define glApplyFramebufferAttachmentCMAAINTEL wrap_glApplyFramebufferAttachmentCMAAINTEL +#define glAttachShader wrap_glAttachShader +#define glBeginConditionalRenderNV wrap_glBeginConditionalRenderNV +#define glBeginPerfMonitorAMD wrap_glBeginPerfMonitorAMD +#define glBeginPerfQueryINTEL wrap_glBeginPerfQueryINTEL +#define glBeginQuery wrap_glBeginQuery +#define glBeginQueryEXT wrap_glBeginQueryEXT +#define glBeginTransformFeedback wrap_glBeginTransformFeedback +#define glBindAttribLocation wrap_glBindAttribLocation +#define glBindBuffer wrap_glBindBuffer +#define glBindBufferBase wrap_glBindBufferBase +#define glBindBufferRange wrap_glBindBufferRange +#define glBindFragDataLocationEXT wrap_glBindFragDataLocationEXT +#define glBindFragDataLocationIndexedEXT wrap_glBindFragDataLocationIndexedEXT +#define glBindFramebuffer wrap_glBindFramebuffer +#define glBindFramebufferOES wrap_glBindFramebufferOES +#define glBindImageTexture wrap_glBindImageTexture +#define glBindProgramPipeline wrap_glBindProgramPipeline +#define glBindProgramPipelineEXT wrap_glBindProgramPipelineEXT +#define glBindRenderbuffer wrap_glBindRenderbuffer +#define glBindRenderbufferOES wrap_glBindRenderbufferOES +#define glBindSampler wrap_glBindSampler +#define glBindTexture wrap_glBindTexture +#define glBindTransformFeedback wrap_glBindTransformFeedback +#define glBindVertexArray wrap_glBindVertexArray +#define glBindVertexArrayOES wrap_glBindVertexArrayOES +#define glBindVertexBuffer wrap_glBindVertexBuffer +#define glBlendBarrier wrap_glBlendBarrier +#define glBlendBarrierKHR wrap_glBlendBarrierKHR +#define glBlendBarrierNV wrap_glBlendBarrierNV +#define glBlendColor wrap_glBlendColor +#define glBlendEquation wrap_glBlendEquation +#define glBlendEquationOES wrap_glBlendEquationOES +#define glBlendEquationSeparate wrap_glBlendEquationSeparate +#define glBlendEquationSeparateOES wrap_glBlendEquationSeparateOES +#define glBlendEquationSeparatei wrap_glBlendEquationSeparatei +#define glBlendEquationSeparateiEXT wrap_glBlendEquationSeparateiEXT +#define glBlendEquationSeparateiOES wrap_glBlendEquationSeparateiOES +#define glBlendEquationi wrap_glBlendEquationi +#define glBlendEquationiEXT wrap_glBlendEquationiEXT +#define glBlendEquationiOES wrap_glBlendEquationiOES +#define glBlendFunc wrap_glBlendFunc +#define glBlendFuncSeparate wrap_glBlendFuncSeparate +#define glBlendFuncSeparateOES wrap_glBlendFuncSeparateOES +#define glBlendFuncSeparatei wrap_glBlendFuncSeparatei +#define glBlendFuncSeparateiEXT wrap_glBlendFuncSeparateiEXT +#define glBlendFuncSeparateiOES wrap_glBlendFuncSeparateiOES +#define glBlendFunci wrap_glBlendFunci +#define glBlendFunciEXT wrap_glBlendFunciEXT +#define glBlendFunciOES wrap_glBlendFunciOES +#define glBlendParameteriNV wrap_glBlendParameteriNV +#define glBlitFramebuffer wrap_glBlitFramebuffer +#define glBlitFramebufferANGLE wrap_glBlitFramebufferANGLE +#define glBlitFramebufferNV wrap_glBlitFramebufferNV +#define glBufferData wrap_glBufferData +#define glBufferStorageEXT wrap_glBufferStorageEXT +#define glBufferSubData wrap_glBufferSubData +#define glCheckFramebufferStatus wrap_glCheckFramebufferStatus +#define glCheckFramebufferStatusOES wrap_glCheckFramebufferStatusOES +#define glClear wrap_glClear +#define glClearBufferfi wrap_glClearBufferfi +#define glClearBufferfv wrap_glClearBufferfv +#define glClearBufferiv wrap_glClearBufferiv +#define glClearBufferuiv wrap_glClearBufferuiv +#define glClearColor wrap_glClearColor +#define glClearColorx wrap_glClearColorx +#define glClearColorxOES wrap_glClearColorxOES +#define glClearDepthf wrap_glClearDepthf +#define glClearDepthfOES wrap_glClearDepthfOES +#define glClearDepthx wrap_glClearDepthx +#define glClearDepthxOES wrap_glClearDepthxOES +#define glClearStencil wrap_glClearStencil +#define glClientActiveTexture wrap_glClientActiveTexture +#define glClientWaitSync wrap_glClientWaitSync +#define glClientWaitSyncAPPLE wrap_glClientWaitSyncAPPLE +#define glClipPlanef wrap_glClipPlanef +#define glClipPlanefIMG wrap_glClipPlanefIMG +#define glClipPlanefOES wrap_glClipPlanefOES +#define glClipPlanex wrap_glClipPlanex +#define glClipPlanexIMG wrap_glClipPlanexIMG +#define glClipPlanexOES wrap_glClipPlanexOES +#define glColor4f wrap_glColor4f +#define glColor4ub wrap_glColor4ub +#define glColor4x wrap_glColor4x +#define glColor4xOES wrap_glColor4xOES +#define glColorMask wrap_glColorMask +#define glColorMaski wrap_glColorMaski +#define glColorMaskiEXT wrap_glColorMaskiEXT +#define glColorMaskiOES wrap_glColorMaskiOES +#define glColorPointer wrap_glColorPointer +#define glCompileShader wrap_glCompileShader +#define glCompressedTexImage2D wrap_glCompressedTexImage2D +#define glCompressedTexImage3D wrap_glCompressedTexImage3D +#define glCompressedTexImage3DOES wrap_glCompressedTexImage3DOES +#define glCompressedTexSubImage2D wrap_glCompressedTexSubImage2D +#define glCompressedTexSubImage3D wrap_glCompressedTexSubImage3D +#define glCompressedTexSubImage3DOES wrap_glCompressedTexSubImage3DOES +#define glCopyBufferSubData wrap_glCopyBufferSubData +#define glCopyBufferSubDataNV wrap_glCopyBufferSubDataNV +#define glCopyImageSubData wrap_glCopyImageSubData +#define glCopyImageSubDataEXT wrap_glCopyImageSubDataEXT +#define glCopyImageSubDataOES wrap_glCopyImageSubDataOES +#define glCopyPathNV wrap_glCopyPathNV +#define glCopyTexImage2D wrap_glCopyTexImage2D +#define glCopyTexSubImage2D wrap_glCopyTexSubImage2D +#define glCopyTexSubImage3D wrap_glCopyTexSubImage3D +#define glCopyTexSubImage3DOES wrap_glCopyTexSubImage3DOES +#define glCopyTextureLevelsAPPLE wrap_glCopyTextureLevelsAPPLE +#define glCoverFillPathInstancedNV wrap_glCoverFillPathInstancedNV +#define glCoverFillPathNV wrap_glCoverFillPathNV +#define glCoverStrokePathInstancedNV wrap_glCoverStrokePathInstancedNV +#define glCoverStrokePathNV wrap_glCoverStrokePathNV +#define glCoverageMaskNV wrap_glCoverageMaskNV +#define glCoverageModulationNV wrap_glCoverageModulationNV +#define glCoverageModulationTableNV wrap_glCoverageModulationTableNV +#define glCoverageOperationNV wrap_glCoverageOperationNV +#define glCreatePerfQueryINTEL wrap_glCreatePerfQueryINTEL +#define glCreateProgram wrap_glCreateProgram +#define glCreateShader wrap_glCreateShader +#define glCreateShaderProgramv wrap_glCreateShaderProgramv +#define glCreateShaderProgramvEXT wrap_glCreateShaderProgramvEXT +#define glCullFace wrap_glCullFace +#define glCurrentPaletteMatrixOES wrap_glCurrentPaletteMatrixOES +#define glDebugMessageCallback wrap_glDebugMessageCallback +#define glDebugMessageCallbackKHR wrap_glDebugMessageCallbackKHR +#define glDebugMessageControl wrap_glDebugMessageControl +#define glDebugMessageControlKHR wrap_glDebugMessageControlKHR +#define glDebugMessageInsert wrap_glDebugMessageInsert +#define glDebugMessageInsertKHR wrap_glDebugMessageInsertKHR +#define glDeleteBuffers wrap_glDeleteBuffers +#define glDeleteFencesNV wrap_glDeleteFencesNV +#define glDeleteFramebuffers wrap_glDeleteFramebuffers +#define glDeleteFramebuffersOES wrap_glDeleteFramebuffersOES +#define glDeletePathsNV wrap_glDeletePathsNV +#define glDeletePerfMonitorsAMD wrap_glDeletePerfMonitorsAMD +#define glDeletePerfQueryINTEL wrap_glDeletePerfQueryINTEL +#define glDeleteProgram wrap_glDeleteProgram +#define glDeleteProgramPipelines wrap_glDeleteProgramPipelines +#define glDeleteProgramPipelinesEXT wrap_glDeleteProgramPipelinesEXT +#define glDeleteQueries wrap_glDeleteQueries +#define glDeleteQueriesEXT wrap_glDeleteQueriesEXT +#define glDeleteRenderbuffers wrap_glDeleteRenderbuffers +#define glDeleteRenderbuffersOES wrap_glDeleteRenderbuffersOES +#define glDeleteSamplers wrap_glDeleteSamplers +#define glDeleteShader wrap_glDeleteShader +#define glDeleteSync wrap_glDeleteSync +#define glDeleteSyncAPPLE wrap_glDeleteSyncAPPLE +#define glDeleteTextures wrap_glDeleteTextures +#define glDeleteTransformFeedbacks wrap_glDeleteTransformFeedbacks +#define glDeleteVertexArrays wrap_glDeleteVertexArrays +#define glDeleteVertexArraysOES wrap_glDeleteVertexArraysOES +#define glDepthFunc wrap_glDepthFunc +#define glDepthMask wrap_glDepthMask +#define glDepthRangeArrayfvNV wrap_glDepthRangeArrayfvNV +#define glDepthRangeIndexedfNV wrap_glDepthRangeIndexedfNV +#define glDepthRangef wrap_glDepthRangef +#define glDepthRangefOES wrap_glDepthRangefOES +#define glDepthRangex wrap_glDepthRangex +#define glDepthRangexOES wrap_glDepthRangexOES +#define glDetachShader wrap_glDetachShader +#define glDisable wrap_glDisable +#define glDisableClientState wrap_glDisableClientState +#define glDisableDriverControlQCOM wrap_glDisableDriverControlQCOM +#define glDisableVertexAttribArray wrap_glDisableVertexAttribArray +#define glDisablei wrap_glDisablei +#define glDisableiEXT wrap_glDisableiEXT +#define glDisableiNV wrap_glDisableiNV +#define glDisableiOES wrap_glDisableiOES +#define glDiscardFramebufferEXT wrap_glDiscardFramebufferEXT +#define glDispatchCompute wrap_glDispatchCompute +#define glDispatchComputeIndirect wrap_glDispatchComputeIndirect +#define glDrawArrays wrap_glDrawArrays +#define glDrawArraysIndirect wrap_glDrawArraysIndirect +#define glDrawArraysInstanced wrap_glDrawArraysInstanced +#define glDrawArraysInstancedANGLE wrap_glDrawArraysInstancedANGLE +#define glDrawArraysInstancedBaseInstanceEXT wrap_glDrawArraysInstancedBaseInstanceEXT +#define glDrawArraysInstancedEXT wrap_glDrawArraysInstancedEXT +#define glDrawArraysInstancedNV wrap_glDrawArraysInstancedNV +#define glDrawBuffers wrap_glDrawBuffers +#define glDrawBuffersEXT wrap_glDrawBuffersEXT +#define glDrawBuffersIndexedEXT wrap_glDrawBuffersIndexedEXT +#define glDrawBuffersNV wrap_glDrawBuffersNV +#define glDrawElements wrap_glDrawElements +#define glDrawElementsBaseVertex wrap_glDrawElementsBaseVertex +#define glDrawElementsBaseVertexEXT wrap_glDrawElementsBaseVertexEXT +#define glDrawElementsBaseVertexOES wrap_glDrawElementsBaseVertexOES +#define glDrawElementsIndirect wrap_glDrawElementsIndirect +#define glDrawElementsInstanced wrap_glDrawElementsInstanced +#define glDrawElementsInstancedANGLE wrap_glDrawElementsInstancedANGLE +#define glDrawElementsInstancedBaseInstanceEXT wrap_glDrawElementsInstancedBaseInstanceEXT +#define glDrawElementsInstancedBaseVertex wrap_glDrawElementsInstancedBaseVertex +#define glDrawElementsInstancedBaseVertexBaseInstanceEXT wrap_glDrawElementsInstancedBaseVertexBaseInstanceEXT +#define glDrawElementsInstancedBaseVertexEXT wrap_glDrawElementsInstancedBaseVertexEXT +#define glDrawElementsInstancedBaseVertexOES wrap_glDrawElementsInstancedBaseVertexOES +#define glDrawElementsInstancedEXT wrap_glDrawElementsInstancedEXT +#define glDrawElementsInstancedNV wrap_glDrawElementsInstancedNV +#define glDrawRangeElements wrap_glDrawRangeElements +#define glDrawRangeElementsBaseVertex wrap_glDrawRangeElementsBaseVertex +#define glDrawRangeElementsBaseVertexEXT wrap_glDrawRangeElementsBaseVertexEXT +#define glDrawRangeElementsBaseVertexOES wrap_glDrawRangeElementsBaseVertexOES +#define glDrawTexfOES wrap_glDrawTexfOES +#define glDrawTexfvOES wrap_glDrawTexfvOES +#define glDrawTexiOES wrap_glDrawTexiOES +#define glDrawTexivOES wrap_glDrawTexivOES +#define glDrawTexsOES wrap_glDrawTexsOES +#define glDrawTexsvOES wrap_glDrawTexsvOES +#define glDrawTexxOES wrap_glDrawTexxOES +#define glDrawTexxvOES wrap_glDrawTexxvOES +#define glEGLImageTargetRenderbufferStorageOES wrap_glEGLImageTargetRenderbufferStorageOES +#define glEGLImageTargetTexture2DOES wrap_glEGLImageTargetTexture2DOES +#define glEnable wrap_glEnable +#define glEnableClientState wrap_glEnableClientState +#define glEnableDriverControlQCOM wrap_glEnableDriverControlQCOM +#define glEnableVertexAttribArray wrap_glEnableVertexAttribArray +#define glEnablei wrap_glEnablei +#define glEnableiEXT wrap_glEnableiEXT +#define glEnableiNV wrap_glEnableiNV +#define glEnableiOES wrap_glEnableiOES +#define glEndConditionalRenderNV wrap_glEndConditionalRenderNV +#define glEndPerfMonitorAMD wrap_glEndPerfMonitorAMD +#define glEndPerfQueryINTEL wrap_glEndPerfQueryINTEL +#define glEndQuery wrap_glEndQuery +#define glEndQueryEXT wrap_glEndQueryEXT +#define glEndTilingQCOM wrap_glEndTilingQCOM +#define glEndTransformFeedback wrap_glEndTransformFeedback +#define glExtGetBufferPointervQCOM wrap_glExtGetBufferPointervQCOM +#define glExtGetBuffersQCOM wrap_glExtGetBuffersQCOM +#define glExtGetFramebuffersQCOM wrap_glExtGetFramebuffersQCOM +#define glExtGetProgramBinarySourceQCOM wrap_glExtGetProgramBinarySourceQCOM +#define glExtGetProgramsQCOM wrap_glExtGetProgramsQCOM +#define glExtGetRenderbuffersQCOM wrap_glExtGetRenderbuffersQCOM +#define glExtGetShadersQCOM wrap_glExtGetShadersQCOM +#define glExtGetTexLevelParameterivQCOM wrap_glExtGetTexLevelParameterivQCOM +#define glExtGetTexSubImageQCOM wrap_glExtGetTexSubImageQCOM +#define glExtGetTexturesQCOM wrap_glExtGetTexturesQCOM +#define glExtIsProgramBinaryQCOM wrap_glExtIsProgramBinaryQCOM +#define glExtTexObjectStateOverrideiQCOM wrap_glExtTexObjectStateOverrideiQCOM +#define glFenceSync wrap_glFenceSync +#define glFenceSyncAPPLE wrap_glFenceSyncAPPLE +#define glFinish wrap_glFinish +#define glFinishFenceNV wrap_glFinishFenceNV +#define glFlush wrap_glFlush +#define glFlushMappedBufferRange wrap_glFlushMappedBufferRange +#define glFlushMappedBufferRangeEXT wrap_glFlushMappedBufferRangeEXT +#define glFogf wrap_glFogf +#define glFogfv wrap_glFogfv +#define glFogx wrap_glFogx +#define glFogxOES wrap_glFogxOES +#define glFogxv wrap_glFogxv +#define glFogxvOES wrap_glFogxvOES +#define glFragmentCoverageColorNV wrap_glFragmentCoverageColorNV +#define glFramebufferParameteri wrap_glFramebufferParameteri +#define glFramebufferRenderbuffer wrap_glFramebufferRenderbuffer +#define glFramebufferRenderbufferOES wrap_glFramebufferRenderbufferOES +#define glFramebufferSampleLocationsfvNV wrap_glFramebufferSampleLocationsfvNV +#define glFramebufferTexture wrap_glFramebufferTexture +#define glFramebufferTexture2D wrap_glFramebufferTexture2D +#define glFramebufferTexture2DMultisampleEXT wrap_glFramebufferTexture2DMultisampleEXT +#define glFramebufferTexture2DMultisampleIMG wrap_glFramebufferTexture2DMultisampleIMG +#define glFramebufferTexture2DOES wrap_glFramebufferTexture2DOES +#define glFramebufferTexture3DOES wrap_glFramebufferTexture3DOES +#define glFramebufferTextureEXT wrap_glFramebufferTextureEXT +#define glFramebufferTextureLayer wrap_glFramebufferTextureLayer +#define glFramebufferTextureMultisampleMultiviewOVR wrap_glFramebufferTextureMultisampleMultiviewOVR +#define glFramebufferTextureMultiviewOVR wrap_glFramebufferTextureMultiviewOVR +#define glFramebufferTextureOES wrap_glFramebufferTextureOES +#define glFrontFace wrap_glFrontFace +#define glFrustumf wrap_glFrustumf +#define glFrustumfOES wrap_glFrustumfOES +#define glFrustumx wrap_glFrustumx +#define glFrustumxOES wrap_glFrustumxOES +#define glGenBuffers wrap_glGenBuffers +#define glGenFencesNV wrap_glGenFencesNV +#define glGenFramebuffers wrap_glGenFramebuffers +#define glGenFramebuffersOES wrap_glGenFramebuffersOES +#define glGenPathsNV wrap_glGenPathsNV +#define glGenPerfMonitorsAMD wrap_glGenPerfMonitorsAMD +#define glGenProgramPipelines wrap_glGenProgramPipelines +#define glGenProgramPipelinesEXT wrap_glGenProgramPipelinesEXT +#define glGenQueries wrap_glGenQueries +#define glGenQueriesEXT wrap_glGenQueriesEXT +#define glGenRenderbuffers wrap_glGenRenderbuffers +#define glGenRenderbuffersOES wrap_glGenRenderbuffersOES +#define glGenSamplers wrap_glGenSamplers +#define glGenTextures wrap_glGenTextures +#define glGenTransformFeedbacks wrap_glGenTransformFeedbacks +#define glGenVertexArrays wrap_glGenVertexArrays +#define glGenVertexArraysOES wrap_glGenVertexArraysOES +#define glGenerateMipmap wrap_glGenerateMipmap +#define glGenerateMipmapOES wrap_glGenerateMipmapOES +#define glGetActiveAttrib wrap_glGetActiveAttrib +#define glGetActiveUniform wrap_glGetActiveUniform +#define glGetActiveUniformBlockName wrap_glGetActiveUniformBlockName +#define glGetActiveUniformBlockiv wrap_glGetActiveUniformBlockiv +#define glGetActiveUniformsiv wrap_glGetActiveUniformsiv +#define glGetAttachedShaders wrap_glGetAttachedShaders +#define glGetAttribLocation wrap_glGetAttribLocation +#define glGetBooleani_v wrap_glGetBooleani_v +#define glGetBooleanv wrap_glGetBooleanv +#define glGetBufferParameteri64v wrap_glGetBufferParameteri64v +#define glGetBufferParameteriv wrap_glGetBufferParameteriv +#define glGetBufferPointerv wrap_glGetBufferPointerv +#define glGetBufferPointervOES wrap_glGetBufferPointervOES +#define glGetClipPlanef wrap_glGetClipPlanef +#define glGetClipPlanefOES wrap_glGetClipPlanefOES +#define glGetClipPlanex wrap_glGetClipPlanex +#define glGetClipPlanexOES wrap_glGetClipPlanexOES +#define glGetCoverageModulationTableNV wrap_glGetCoverageModulationTableNV +#define glGetDebugMessageLog wrap_glGetDebugMessageLog +#define glGetDebugMessageLogKHR wrap_glGetDebugMessageLogKHR +#define glGetDriverControlStringQCOM wrap_glGetDriverControlStringQCOM +#define glGetDriverControlsQCOM wrap_glGetDriverControlsQCOM +#define glGetError wrap_glGetError +#define glGetFenceivNV wrap_glGetFenceivNV +#define glGetFirstPerfQueryIdINTEL wrap_glGetFirstPerfQueryIdINTEL +#define glGetFixedv wrap_glGetFixedv +#define glGetFixedvOES wrap_glGetFixedvOES +#define glGetFloati_vNV wrap_glGetFloati_vNV +#define glGetFloatv wrap_glGetFloatv +#define glGetFragDataIndexEXT wrap_glGetFragDataIndexEXT +#define glGetFragDataLocation wrap_glGetFragDataLocation +#define glGetFramebufferAttachmentParameteriv wrap_glGetFramebufferAttachmentParameteriv +#define glGetFramebufferAttachmentParameterivOES wrap_glGetFramebufferAttachmentParameterivOES +#define glGetFramebufferParameteriv wrap_glGetFramebufferParameteriv +#define glGetGraphicsResetStatus wrap_glGetGraphicsResetStatus +#define glGetGraphicsResetStatusEXT wrap_glGetGraphicsResetStatusEXT +#define glGetGraphicsResetStatusKHR wrap_glGetGraphicsResetStatusKHR +#define glGetImageHandleNV wrap_glGetImageHandleNV +#define glGetInteger64i_v wrap_glGetInteger64i_v +#define glGetInteger64v wrap_glGetInteger64v +#define glGetInteger64vAPPLE wrap_glGetInteger64vAPPLE +#define glGetIntegeri_v wrap_glGetIntegeri_v +#define glGetIntegeri_vEXT wrap_glGetIntegeri_vEXT +#define glGetIntegerv wrap_glGetIntegerv +#define glGetInternalformatSampleivNV wrap_glGetInternalformatSampleivNV +#define glGetInternalformativ wrap_glGetInternalformativ +#define glGetLightfv wrap_glGetLightfv +#define glGetLightxv wrap_glGetLightxv +#define glGetLightxvOES wrap_glGetLightxvOES +#define glGetMaterialfv wrap_glGetMaterialfv +#define glGetMaterialxv wrap_glGetMaterialxv +#define glGetMaterialxvOES wrap_glGetMaterialxvOES +#define glGetMultisamplefv wrap_glGetMultisamplefv +#define glGetNextPerfQueryIdINTEL wrap_glGetNextPerfQueryIdINTEL +#define glGetObjectLabel wrap_glGetObjectLabel +#define glGetObjectLabelEXT wrap_glGetObjectLabelEXT +#define glGetObjectLabelKHR wrap_glGetObjectLabelKHR +#define glGetObjectPtrLabel wrap_glGetObjectPtrLabel +#define glGetObjectPtrLabelKHR wrap_glGetObjectPtrLabelKHR +#define glGetPathCommandsNV wrap_glGetPathCommandsNV +#define glGetPathCoordsNV wrap_glGetPathCoordsNV +#define glGetPathDashArrayNV wrap_glGetPathDashArrayNV +#define glGetPathLengthNV wrap_glGetPathLengthNV +#define glGetPathMetricRangeNV wrap_glGetPathMetricRangeNV +#define glGetPathMetricsNV wrap_glGetPathMetricsNV +#define glGetPathParameterfvNV wrap_glGetPathParameterfvNV +#define glGetPathParameterivNV wrap_glGetPathParameterivNV +#define glGetPathSpacingNV wrap_glGetPathSpacingNV +#define glGetPerfCounterInfoINTEL wrap_glGetPerfCounterInfoINTEL +#define glGetPerfMonitorCounterDataAMD wrap_glGetPerfMonitorCounterDataAMD +#define glGetPerfMonitorCounterInfoAMD wrap_glGetPerfMonitorCounterInfoAMD +#define glGetPerfMonitorCounterStringAMD wrap_glGetPerfMonitorCounterStringAMD +#define glGetPerfMonitorCountersAMD wrap_glGetPerfMonitorCountersAMD +#define glGetPerfMonitorGroupStringAMD wrap_glGetPerfMonitorGroupStringAMD +#define glGetPerfMonitorGroupsAMD wrap_glGetPerfMonitorGroupsAMD +#define glGetPerfQueryDataINTEL wrap_glGetPerfQueryDataINTEL +#define glGetPerfQueryIdByNameINTEL wrap_glGetPerfQueryIdByNameINTEL +#define glGetPerfQueryInfoINTEL wrap_glGetPerfQueryInfoINTEL +#define glGetPointerv wrap_glGetPointerv +#define glGetPointervKHR wrap_glGetPointervKHR +#define glGetProgramBinary wrap_glGetProgramBinary +#define glGetProgramBinaryOES wrap_glGetProgramBinaryOES +#define glGetProgramInfoLog wrap_glGetProgramInfoLog +#define glGetProgramInterfaceiv wrap_glGetProgramInterfaceiv +#define glGetProgramPipelineInfoLog wrap_glGetProgramPipelineInfoLog +#define glGetProgramPipelineInfoLogEXT wrap_glGetProgramPipelineInfoLogEXT +#define glGetProgramPipelineiv wrap_glGetProgramPipelineiv +#define glGetProgramPipelineivEXT wrap_glGetProgramPipelineivEXT +#define glGetProgramResourceIndex wrap_glGetProgramResourceIndex +#define glGetProgramResourceLocation wrap_glGetProgramResourceLocation +#define glGetProgramResourceLocationIndexEXT wrap_glGetProgramResourceLocationIndexEXT +#define glGetProgramResourceName wrap_glGetProgramResourceName +#define glGetProgramResourcefvNV wrap_glGetProgramResourcefvNV +#define glGetProgramResourceiv wrap_glGetProgramResourceiv +#define glGetProgramiv wrap_glGetProgramiv +#define glGetQueryObjecti64vEXT wrap_glGetQueryObjecti64vEXT +#define glGetQueryObjectivEXT wrap_glGetQueryObjectivEXT +#define glGetQueryObjectui64vEXT wrap_glGetQueryObjectui64vEXT +#define glGetQueryObjectuiv wrap_glGetQueryObjectuiv +#define glGetQueryObjectuivEXT wrap_glGetQueryObjectuivEXT +#define glGetQueryiv wrap_glGetQueryiv +#define glGetQueryivEXT wrap_glGetQueryivEXT +#define glGetRenderbufferParameteriv wrap_glGetRenderbufferParameteriv +#define glGetRenderbufferParameterivOES wrap_glGetRenderbufferParameterivOES +#define glGetSamplerParameterIiv wrap_glGetSamplerParameterIiv +#define glGetSamplerParameterIivEXT wrap_glGetSamplerParameterIivEXT +#define glGetSamplerParameterIivOES wrap_glGetSamplerParameterIivOES +#define glGetSamplerParameterIuiv wrap_glGetSamplerParameterIuiv +#define glGetSamplerParameterIuivEXT wrap_glGetSamplerParameterIuivEXT +#define glGetSamplerParameterIuivOES wrap_glGetSamplerParameterIuivOES +#define glGetSamplerParameterfv wrap_glGetSamplerParameterfv +#define glGetSamplerParameteriv wrap_glGetSamplerParameteriv +#define glGetShaderInfoLog wrap_glGetShaderInfoLog +#define glGetShaderPrecisionFormat wrap_glGetShaderPrecisionFormat +#define glGetShaderSource wrap_glGetShaderSource +#define glGetShaderiv wrap_glGetShaderiv +#define glGetString wrap_glGetString +#define glGetStringi wrap_glGetStringi +#define glGetSynciv wrap_glGetSynciv +#define glGetSyncivAPPLE wrap_glGetSyncivAPPLE +#define glGetTexEnvfv wrap_glGetTexEnvfv +#define glGetTexEnviv wrap_glGetTexEnviv +#define glGetTexEnvxv wrap_glGetTexEnvxv +#define glGetTexEnvxvOES wrap_glGetTexEnvxvOES +#define glGetTexGenfvOES wrap_glGetTexGenfvOES +#define glGetTexGenivOES wrap_glGetTexGenivOES +#define glGetTexGenxvOES wrap_glGetTexGenxvOES +#define glGetTexLevelParameterfv wrap_glGetTexLevelParameterfv +#define glGetTexLevelParameteriv wrap_glGetTexLevelParameteriv +#define glGetTexParameterIiv wrap_glGetTexParameterIiv +#define glGetTexParameterIivEXT wrap_glGetTexParameterIivEXT +#define glGetTexParameterIivOES wrap_glGetTexParameterIivOES +#define glGetTexParameterIuiv wrap_glGetTexParameterIuiv +#define glGetTexParameterIuivEXT wrap_glGetTexParameterIuivEXT +#define glGetTexParameterIuivOES wrap_glGetTexParameterIuivOES +#define glGetTexParameterfv wrap_glGetTexParameterfv +#define glGetTexParameteriv wrap_glGetTexParameteriv +#define glGetTexParameterxv wrap_glGetTexParameterxv +#define glGetTexParameterxvOES wrap_glGetTexParameterxvOES +#define glGetTextureHandleNV wrap_glGetTextureHandleNV +#define glGetTextureSamplerHandleNV wrap_glGetTextureSamplerHandleNV +#define glGetTransformFeedbackVarying wrap_glGetTransformFeedbackVarying +#define glGetTranslatedShaderSourceANGLE wrap_glGetTranslatedShaderSourceANGLE +#define glGetUniformBlockIndex wrap_glGetUniformBlockIndex +#define glGetUniformIndices wrap_glGetUniformIndices +#define glGetUniformLocation wrap_glGetUniformLocation +#define glGetUniformfv wrap_glGetUniformfv +#define glGetUniformiv wrap_glGetUniformiv +#define glGetUniformuiv wrap_glGetUniformuiv +#define glGetVertexAttribIiv wrap_glGetVertexAttribIiv +#define glGetVertexAttribIuiv wrap_glGetVertexAttribIuiv +#define glGetVertexAttribPointerv wrap_glGetVertexAttribPointerv +#define glGetVertexAttribfv wrap_glGetVertexAttribfv +#define glGetVertexAttribiv wrap_glGetVertexAttribiv +#define glGetnUniformfv wrap_glGetnUniformfv +#define glGetnUniformfvEXT wrap_glGetnUniformfvEXT +#define glGetnUniformfvKHR wrap_glGetnUniformfvKHR +#define glGetnUniformiv wrap_glGetnUniformiv +#define glGetnUniformivEXT wrap_glGetnUniformivEXT +#define glGetnUniformivKHR wrap_glGetnUniformivKHR +#define glGetnUniformuiv wrap_glGetnUniformuiv +#define glGetnUniformuivKHR wrap_glGetnUniformuivKHR +#define glHint wrap_glHint +#define glInsertEventMarkerEXT wrap_glInsertEventMarkerEXT +#define glInterpolatePathsNV wrap_glInterpolatePathsNV +#define glInvalidateFramebuffer wrap_glInvalidateFramebuffer +#define glInvalidateSubFramebuffer wrap_glInvalidateSubFramebuffer +#define glIsBuffer wrap_glIsBuffer +#define glIsEnabled wrap_glIsEnabled +#define glIsEnabledi wrap_glIsEnabledi +#define glIsEnablediEXT wrap_glIsEnablediEXT +#define glIsEnablediNV wrap_glIsEnablediNV +#define glIsEnablediOES wrap_glIsEnablediOES +#define glIsFenceNV wrap_glIsFenceNV +#define glIsFramebuffer wrap_glIsFramebuffer +#define glIsFramebufferOES wrap_glIsFramebufferOES +#define glIsImageHandleResidentNV wrap_glIsImageHandleResidentNV +#define glIsPathNV wrap_glIsPathNV +#define glIsPointInFillPathNV wrap_glIsPointInFillPathNV +#define glIsPointInStrokePathNV wrap_glIsPointInStrokePathNV +#define glIsProgram wrap_glIsProgram +#define glIsProgramPipeline wrap_glIsProgramPipeline +#define glIsProgramPipelineEXT wrap_glIsProgramPipelineEXT +#define glIsQuery wrap_glIsQuery +#define glIsQueryEXT wrap_glIsQueryEXT +#define glIsRenderbuffer wrap_glIsRenderbuffer +#define glIsRenderbufferOES wrap_glIsRenderbufferOES +#define glIsSampler wrap_glIsSampler +#define glIsShader wrap_glIsShader +#define glIsSync wrap_glIsSync +#define glIsSyncAPPLE wrap_glIsSyncAPPLE +#define glIsTexture wrap_glIsTexture +#define glIsTextureHandleResidentNV wrap_glIsTextureHandleResidentNV +#define glIsTransformFeedback wrap_glIsTransformFeedback +#define glIsVertexArray wrap_glIsVertexArray +#define glIsVertexArrayOES wrap_glIsVertexArrayOES +#define glLabelObjectEXT wrap_glLabelObjectEXT +#define glLightModelf wrap_glLightModelf +#define glLightModelfv wrap_glLightModelfv +#define glLightModelx wrap_glLightModelx +#define glLightModelxOES wrap_glLightModelxOES +#define glLightModelxv wrap_glLightModelxv +#define glLightModelxvOES wrap_glLightModelxvOES +#define glLightf wrap_glLightf +#define glLightfv wrap_glLightfv +#define glLightx wrap_glLightx +#define glLightxOES wrap_glLightxOES +#define glLightxv wrap_glLightxv +#define glLightxvOES wrap_glLightxvOES +#define glLineWidth wrap_glLineWidth +#define glLineWidthx wrap_glLineWidthx +#define glLineWidthxOES wrap_glLineWidthxOES +#define glLinkProgram wrap_glLinkProgram +#define glLoadIdentity wrap_glLoadIdentity +#define glLoadMatrixf wrap_glLoadMatrixf +#define glLoadMatrixx wrap_glLoadMatrixx +#define glLoadMatrixxOES wrap_glLoadMatrixxOES +#define glLoadPaletteFromModelViewMatrixOES wrap_glLoadPaletteFromModelViewMatrixOES +#define glLogicOp wrap_glLogicOp +#define glMakeImageHandleNonResidentNV wrap_glMakeImageHandleNonResidentNV +#define glMakeImageHandleResidentNV wrap_glMakeImageHandleResidentNV +#define glMakeTextureHandleNonResidentNV wrap_glMakeTextureHandleNonResidentNV +#define glMakeTextureHandleResidentNV wrap_glMakeTextureHandleResidentNV +#define glMapBufferOES wrap_glMapBufferOES +#define glMapBufferRange wrap_glMapBufferRange +#define glMapBufferRangeEXT wrap_glMapBufferRangeEXT +#define glMaterialf wrap_glMaterialf +#define glMaterialfv wrap_glMaterialfv +#define glMaterialx wrap_glMaterialx +#define glMaterialxOES wrap_glMaterialxOES +#define glMaterialxv wrap_glMaterialxv +#define glMaterialxvOES wrap_glMaterialxvOES +#define glMatrixIndexPointerOES wrap_glMatrixIndexPointerOES +#define glMatrixLoad3x2fNV wrap_glMatrixLoad3x2fNV +#define glMatrixLoad3x3fNV wrap_glMatrixLoad3x3fNV +#define glMatrixLoadTranspose3x3fNV wrap_glMatrixLoadTranspose3x3fNV +#define glMatrixMode wrap_glMatrixMode +#define glMatrixMult3x2fNV wrap_glMatrixMult3x2fNV +#define glMatrixMult3x3fNV wrap_glMatrixMult3x3fNV +#define glMatrixMultTranspose3x3fNV wrap_glMatrixMultTranspose3x3fNV +#define glMemoryBarrier wrap_glMemoryBarrier +#define glMemoryBarrierByRegion wrap_glMemoryBarrierByRegion +#define glMinSampleShading wrap_glMinSampleShading +#define glMinSampleShadingOES wrap_glMinSampleShadingOES +#define glMultMatrixf wrap_glMultMatrixf +#define glMultMatrixx wrap_glMultMatrixx +#define glMultMatrixxOES wrap_glMultMatrixxOES +#define glMultiDrawArraysEXT wrap_glMultiDrawArraysEXT +#define glMultiDrawArraysIndirectEXT wrap_glMultiDrawArraysIndirectEXT +#define glMultiDrawElementsBaseVertexEXT wrap_glMultiDrawElementsBaseVertexEXT +#define glMultiDrawElementsBaseVertexOES wrap_glMultiDrawElementsBaseVertexOES +#define glMultiDrawElementsEXT wrap_glMultiDrawElementsEXT +#define glMultiDrawElementsIndirectEXT wrap_glMultiDrawElementsIndirectEXT +#define glMultiTexCoord4f wrap_glMultiTexCoord4f +#define glMultiTexCoord4x wrap_glMultiTexCoord4x +#define glMultiTexCoord4xOES wrap_glMultiTexCoord4xOES +#define glNamedFramebufferSampleLocationsfvNV wrap_glNamedFramebufferSampleLocationsfvNV +#define glNormal3f wrap_glNormal3f +#define glNormal3x wrap_glNormal3x +#define glNormal3xOES wrap_glNormal3xOES +#define glNormalPointer wrap_glNormalPointer +#define glObjectLabel wrap_glObjectLabel +#define glObjectLabelKHR wrap_glObjectLabelKHR +#define glObjectPtrLabel wrap_glObjectPtrLabel +#define glObjectPtrLabelKHR wrap_glObjectPtrLabelKHR +#define glOrthof wrap_glOrthof +#define glOrthofOES wrap_glOrthofOES +#define glOrthox wrap_glOrthox +#define glOrthoxOES wrap_glOrthoxOES +#define glPatchParameteri wrap_glPatchParameteri +#define glPatchParameteriEXT wrap_glPatchParameteriEXT +#define glPatchParameteriOES wrap_glPatchParameteriOES +#define glPathCommandsNV wrap_glPathCommandsNV +#define glPathCoordsNV wrap_glPathCoordsNV +#define glPathCoverDepthFuncNV wrap_glPathCoverDepthFuncNV +#define glPathDashArrayNV wrap_glPathDashArrayNV +#define glPathGlyphIndexArrayNV wrap_glPathGlyphIndexArrayNV +#define glPathGlyphIndexRangeNV wrap_glPathGlyphIndexRangeNV +#define glPathGlyphRangeNV wrap_glPathGlyphRangeNV +#define glPathGlyphsNV wrap_glPathGlyphsNV +#define glPathMemoryGlyphIndexArrayNV wrap_glPathMemoryGlyphIndexArrayNV +#define glPathParameterfNV wrap_glPathParameterfNV +#define glPathParameterfvNV wrap_glPathParameterfvNV +#define glPathParameteriNV wrap_glPathParameteriNV +#define glPathParameterivNV wrap_glPathParameterivNV +#define glPathStencilDepthOffsetNV wrap_glPathStencilDepthOffsetNV +#define glPathStencilFuncNV wrap_glPathStencilFuncNV +#define glPathStringNV wrap_glPathStringNV +#define glPathSubCommandsNV wrap_glPathSubCommandsNV +#define glPathSubCoordsNV wrap_glPathSubCoordsNV +#define glPauseTransformFeedback wrap_glPauseTransformFeedback +#define glPixelStorei wrap_glPixelStorei +#define glPointAlongPathNV wrap_glPointAlongPathNV +#define glPointParameterf wrap_glPointParameterf +#define glPointParameterfv wrap_glPointParameterfv +#define glPointParameterx wrap_glPointParameterx +#define glPointParameterxOES wrap_glPointParameterxOES +#define glPointParameterxv wrap_glPointParameterxv +#define glPointParameterxvOES wrap_glPointParameterxvOES +#define glPointSize wrap_glPointSize +#define glPointSizePointerOES wrap_glPointSizePointerOES +#define glPointSizex wrap_glPointSizex +#define glPointSizexOES wrap_glPointSizexOES +#define glPolygonModeNV wrap_glPolygonModeNV +#define glPolygonOffset wrap_glPolygonOffset +#define glPolygonOffsetx wrap_glPolygonOffsetx +#define glPolygonOffsetxOES wrap_glPolygonOffsetxOES +#define glPopDebugGroup wrap_glPopDebugGroup +#define glPopDebugGroupKHR wrap_glPopDebugGroupKHR +#define glPopGroupMarkerEXT wrap_glPopGroupMarkerEXT +#define glPopMatrix wrap_glPopMatrix +#define glPrimitiveBoundingBox wrap_glPrimitiveBoundingBox +#define glPrimitiveBoundingBoxEXT wrap_glPrimitiveBoundingBoxEXT +#define glPrimitiveBoundingBoxOES wrap_glPrimitiveBoundingBoxOES +#define glProgramBinary wrap_glProgramBinary +#define glProgramBinaryOES wrap_glProgramBinaryOES +#define glProgramParameteri wrap_glProgramParameteri +#define glProgramParameteriEXT wrap_glProgramParameteriEXT +#define glProgramPathFragmentInputGenNV wrap_glProgramPathFragmentInputGenNV +#define glProgramUniform1f wrap_glProgramUniform1f +#define glProgramUniform1fEXT wrap_glProgramUniform1fEXT +#define glProgramUniform1fv wrap_glProgramUniform1fv +#define glProgramUniform1fvEXT wrap_glProgramUniform1fvEXT +#define glProgramUniform1i wrap_glProgramUniform1i +#define glProgramUniform1iEXT wrap_glProgramUniform1iEXT +#define glProgramUniform1iv wrap_glProgramUniform1iv +#define glProgramUniform1ivEXT wrap_glProgramUniform1ivEXT +#define glProgramUniform1ui wrap_glProgramUniform1ui +#define glProgramUniform1uiEXT wrap_glProgramUniform1uiEXT +#define glProgramUniform1uiv wrap_glProgramUniform1uiv +#define glProgramUniform1uivEXT wrap_glProgramUniform1uivEXT +#define glProgramUniform2f wrap_glProgramUniform2f +#define glProgramUniform2fEXT wrap_glProgramUniform2fEXT +#define glProgramUniform2fv wrap_glProgramUniform2fv +#define glProgramUniform2fvEXT wrap_glProgramUniform2fvEXT +#define glProgramUniform2i wrap_glProgramUniform2i +#define glProgramUniform2iEXT wrap_glProgramUniform2iEXT +#define glProgramUniform2iv wrap_glProgramUniform2iv +#define glProgramUniform2ivEXT wrap_glProgramUniform2ivEXT +#define glProgramUniform2ui wrap_glProgramUniform2ui +#define glProgramUniform2uiEXT wrap_glProgramUniform2uiEXT +#define glProgramUniform2uiv wrap_glProgramUniform2uiv +#define glProgramUniform2uivEXT wrap_glProgramUniform2uivEXT +#define glProgramUniform3f wrap_glProgramUniform3f +#define glProgramUniform3fEXT wrap_glProgramUniform3fEXT +#define glProgramUniform3fv wrap_glProgramUniform3fv +#define glProgramUniform3fvEXT wrap_glProgramUniform3fvEXT +#define glProgramUniform3i wrap_glProgramUniform3i +#define glProgramUniform3iEXT wrap_glProgramUniform3iEXT +#define glProgramUniform3iv wrap_glProgramUniform3iv +#define glProgramUniform3ivEXT wrap_glProgramUniform3ivEXT +#define glProgramUniform3ui wrap_glProgramUniform3ui +#define glProgramUniform3uiEXT wrap_glProgramUniform3uiEXT +#define glProgramUniform3uiv wrap_glProgramUniform3uiv +#define glProgramUniform3uivEXT wrap_glProgramUniform3uivEXT +#define glProgramUniform4f wrap_glProgramUniform4f +#define glProgramUniform4fEXT wrap_glProgramUniform4fEXT +#define glProgramUniform4fv wrap_glProgramUniform4fv +#define glProgramUniform4fvEXT wrap_glProgramUniform4fvEXT +#define glProgramUniform4i wrap_glProgramUniform4i +#define glProgramUniform4iEXT wrap_glProgramUniform4iEXT +#define glProgramUniform4iv wrap_glProgramUniform4iv +#define glProgramUniform4ivEXT wrap_glProgramUniform4ivEXT +#define glProgramUniform4ui wrap_glProgramUniform4ui +#define glProgramUniform4uiEXT wrap_glProgramUniform4uiEXT +#define glProgramUniform4uiv wrap_glProgramUniform4uiv +#define glProgramUniform4uivEXT wrap_glProgramUniform4uivEXT +#define glProgramUniformHandleui64NV wrap_glProgramUniformHandleui64NV +#define glProgramUniformHandleui64vNV wrap_glProgramUniformHandleui64vNV +#define glProgramUniformMatrix2fv wrap_glProgramUniformMatrix2fv +#define glProgramUniformMatrix2fvEXT wrap_glProgramUniformMatrix2fvEXT +#define glProgramUniformMatrix2x3fv wrap_glProgramUniformMatrix2x3fv +#define glProgramUniformMatrix2x3fvEXT wrap_glProgramUniformMatrix2x3fvEXT +#define glProgramUniformMatrix2x4fv wrap_glProgramUniformMatrix2x4fv +#define glProgramUniformMatrix2x4fvEXT wrap_glProgramUniformMatrix2x4fvEXT +#define glProgramUniformMatrix3fv wrap_glProgramUniformMatrix3fv +#define glProgramUniformMatrix3fvEXT wrap_glProgramUniformMatrix3fvEXT +#define glProgramUniformMatrix3x2fv wrap_glProgramUniformMatrix3x2fv +#define glProgramUniformMatrix3x2fvEXT wrap_glProgramUniformMatrix3x2fvEXT +#define glProgramUniformMatrix3x4fv wrap_glProgramUniformMatrix3x4fv +#define glProgramUniformMatrix3x4fvEXT wrap_glProgramUniformMatrix3x4fvEXT +#define glProgramUniformMatrix4fv wrap_glProgramUniformMatrix4fv +#define glProgramUniformMatrix4fvEXT wrap_glProgramUniformMatrix4fvEXT +#define glProgramUniformMatrix4x2fv wrap_glProgramUniformMatrix4x2fv +#define glProgramUniformMatrix4x2fvEXT wrap_glProgramUniformMatrix4x2fvEXT +#define glProgramUniformMatrix4x3fv wrap_glProgramUniformMatrix4x3fv +#define glProgramUniformMatrix4x3fvEXT wrap_glProgramUniformMatrix4x3fvEXT +#define glPushDebugGroup wrap_glPushDebugGroup +#define glPushDebugGroupKHR wrap_glPushDebugGroupKHR +#define glPushGroupMarkerEXT wrap_glPushGroupMarkerEXT +#define glPushMatrix wrap_glPushMatrix +#define glQueryCounterEXT wrap_glQueryCounterEXT +#define glQueryMatrixxOES wrap_glQueryMatrixxOES +#define glRasterSamplesEXT wrap_glRasterSamplesEXT +#define glReadBuffer wrap_glReadBuffer +#define glReadBufferIndexedEXT wrap_glReadBufferIndexedEXT +#define glReadBufferNV wrap_glReadBufferNV +#define glReadPixels wrap_glReadPixels +#define glReadnPixels wrap_glReadnPixels +#define glReadnPixelsEXT wrap_glReadnPixelsEXT +#define glReadnPixelsKHR wrap_glReadnPixelsKHR +#define glReleaseShaderCompiler wrap_glReleaseShaderCompiler +#define glRenderbufferStorage wrap_glRenderbufferStorage +#define glRenderbufferStorageMultisample wrap_glRenderbufferStorageMultisample +#define glRenderbufferStorageMultisampleANGLE wrap_glRenderbufferStorageMultisampleANGLE +#define glRenderbufferStorageMultisampleAPPLE wrap_glRenderbufferStorageMultisampleAPPLE +#define glRenderbufferStorageMultisampleEXT wrap_glRenderbufferStorageMultisampleEXT +#define glRenderbufferStorageMultisampleIMG wrap_glRenderbufferStorageMultisampleIMG +#define glRenderbufferStorageMultisampleNV wrap_glRenderbufferStorageMultisampleNV +#define glRenderbufferStorageOES wrap_glRenderbufferStorageOES +#define glResolveDepthValuesNV wrap_glResolveDepthValuesNV +#define glResolveMultisampleFramebufferAPPLE wrap_glResolveMultisampleFramebufferAPPLE +#define glResumeTransformFeedback wrap_glResumeTransformFeedback +#define glRotatef wrap_glRotatef +#define glRotatex wrap_glRotatex +#define glRotatexOES wrap_glRotatexOES +#define glSampleCoverage wrap_glSampleCoverage +#define glSampleCoveragex wrap_glSampleCoveragex +#define glSampleCoveragexOES wrap_glSampleCoveragexOES +#define glSampleMaski wrap_glSampleMaski +#define glSamplerParameterIiv wrap_glSamplerParameterIiv +#define glSamplerParameterIivEXT wrap_glSamplerParameterIivEXT +#define glSamplerParameterIivOES wrap_glSamplerParameterIivOES +#define glSamplerParameterIuiv wrap_glSamplerParameterIuiv +#define glSamplerParameterIuivEXT wrap_glSamplerParameterIuivEXT +#define glSamplerParameterIuivOES wrap_glSamplerParameterIuivOES +#define glSamplerParameterf wrap_glSamplerParameterf +#define glSamplerParameterfv wrap_glSamplerParameterfv +#define glSamplerParameteri wrap_glSamplerParameteri +#define glSamplerParameteriv wrap_glSamplerParameteriv +#define glScalef wrap_glScalef +#define glScalex wrap_glScalex +#define glScalexOES wrap_glScalexOES +#define glScissor wrap_glScissor +#define glScissorArrayvNV wrap_glScissorArrayvNV +#define glScissorIndexedNV wrap_glScissorIndexedNV +#define glScissorIndexedvNV wrap_glScissorIndexedvNV +#define glSelectPerfMonitorCountersAMD wrap_glSelectPerfMonitorCountersAMD +#define glSetFenceNV wrap_glSetFenceNV +#define glShadeModel wrap_glShadeModel +#define glShaderBinary wrap_glShaderBinary +#define glShaderSource wrap_glShaderSource +#define glStartTilingQCOM wrap_glStartTilingQCOM +#define glStencilFillPathInstancedNV wrap_glStencilFillPathInstancedNV +#define glStencilFillPathNV wrap_glStencilFillPathNV +#define glStencilFunc wrap_glStencilFunc +#define glStencilFuncSeparate wrap_glStencilFuncSeparate +#define glStencilMask wrap_glStencilMask +#define glStencilMaskSeparate wrap_glStencilMaskSeparate +#define glStencilOp wrap_glStencilOp +#define glStencilOpSeparate wrap_glStencilOpSeparate +#define glStencilStrokePathInstancedNV wrap_glStencilStrokePathInstancedNV +#define glStencilStrokePathNV wrap_glStencilStrokePathNV +#define glStencilThenCoverFillPathInstancedNV wrap_glStencilThenCoverFillPathInstancedNV +#define glStencilThenCoverFillPathNV wrap_glStencilThenCoverFillPathNV +#define glStencilThenCoverStrokePathInstancedNV wrap_glStencilThenCoverStrokePathInstancedNV +#define glStencilThenCoverStrokePathNV wrap_glStencilThenCoverStrokePathNV +#define glSubpixelPrecisionBiasNV wrap_glSubpixelPrecisionBiasNV +#define glTestFenceNV wrap_glTestFenceNV +#define glTexBuffer wrap_glTexBuffer +#define glTexBufferEXT wrap_glTexBufferEXT +#define glTexBufferOES wrap_glTexBufferOES +#define glTexBufferRange wrap_glTexBufferRange +#define glTexBufferRangeEXT wrap_glTexBufferRangeEXT +#define glTexBufferRangeOES wrap_glTexBufferRangeOES +#define glTexCoordPointer wrap_glTexCoordPointer +#define glTexEnvf wrap_glTexEnvf +#define glTexEnvfv wrap_glTexEnvfv +#define glTexEnvi wrap_glTexEnvi +#define glTexEnviv wrap_glTexEnviv +#define glTexEnvx wrap_glTexEnvx +#define glTexEnvxOES wrap_glTexEnvxOES +#define glTexEnvxv wrap_glTexEnvxv +#define glTexEnvxvOES wrap_glTexEnvxvOES +#define glTexGenfOES wrap_glTexGenfOES +#define glTexGenfvOES wrap_glTexGenfvOES +#define glTexGeniOES wrap_glTexGeniOES +#define glTexGenivOES wrap_glTexGenivOES +#define glTexGenxOES wrap_glTexGenxOES +#define glTexGenxvOES wrap_glTexGenxvOES +#define glTexImage2D wrap_glTexImage2D +#define glTexImage3D wrap_glTexImage3D +#define glTexImage3DOES wrap_glTexImage3DOES +#define glTexPageCommitmentEXT wrap_glTexPageCommitmentEXT +#define glTexParameterIiv wrap_glTexParameterIiv +#define glTexParameterIivEXT wrap_glTexParameterIivEXT +#define glTexParameterIivOES wrap_glTexParameterIivOES +#define glTexParameterIuiv wrap_glTexParameterIuiv +#define glTexParameterIuivEXT wrap_glTexParameterIuivEXT +#define glTexParameterIuivOES wrap_glTexParameterIuivOES +#define glTexParameterf wrap_glTexParameterf +#define glTexParameterfv wrap_glTexParameterfv +#define glTexParameteri wrap_glTexParameteri +#define glTexParameteriv wrap_glTexParameteriv +#define glTexParameterx wrap_glTexParameterx +#define glTexParameterxOES wrap_glTexParameterxOES +#define glTexParameterxv wrap_glTexParameterxv +#define glTexParameterxvOES wrap_glTexParameterxvOES +#define glTexStorage1DEXT wrap_glTexStorage1DEXT +#define glTexStorage2D wrap_glTexStorage2D +#define glTexStorage2DEXT wrap_glTexStorage2DEXT +#define glTexStorage2DMultisample wrap_glTexStorage2DMultisample +#define glTexStorage3D wrap_glTexStorage3D +#define glTexStorage3DEXT wrap_glTexStorage3DEXT +#define glTexStorage3DMultisample wrap_glTexStorage3DMultisample +#define glTexStorage3DMultisampleOES wrap_glTexStorage3DMultisampleOES +#define glTexSubImage2D wrap_glTexSubImage2D +#define glTexSubImage3D wrap_glTexSubImage3D +#define glTexSubImage3DOES wrap_glTexSubImage3DOES +#define glTextureStorage1DEXT wrap_glTextureStorage1DEXT +#define glTextureStorage2DEXT wrap_glTextureStorage2DEXT +#define glTextureStorage3DEXT wrap_glTextureStorage3DEXT +#define glTextureViewEXT wrap_glTextureViewEXT +#define glTextureViewOES wrap_glTextureViewOES +#define glTransformFeedbackVaryings wrap_glTransformFeedbackVaryings +#define glTransformPathNV wrap_glTransformPathNV +#define glTranslatef wrap_glTranslatef +#define glTranslatex wrap_glTranslatex +#define glTranslatexOES wrap_glTranslatexOES +#define glUniform1f wrap_glUniform1f +#define glUniform1fv wrap_glUniform1fv +#define glUniform1i wrap_glUniform1i +#define glUniform1iv wrap_glUniform1iv +#define glUniform1ui wrap_glUniform1ui +#define glUniform1uiv wrap_glUniform1uiv +#define glUniform2f wrap_glUniform2f +#define glUniform2fv wrap_glUniform2fv +#define glUniform2i wrap_glUniform2i +#define glUniform2iv wrap_glUniform2iv +#define glUniform2ui wrap_glUniform2ui +#define glUniform2uiv wrap_glUniform2uiv +#define glUniform3f wrap_glUniform3f +#define glUniform3fv wrap_glUniform3fv +#define glUniform3i wrap_glUniform3i +#define glUniform3iv wrap_glUniform3iv +#define glUniform3ui wrap_glUniform3ui +#define glUniform3uiv wrap_glUniform3uiv +#define glUniform4f wrap_glUniform4f +#define glUniform4fv wrap_glUniform4fv +#define glUniform4i wrap_glUniform4i +#define glUniform4iv wrap_glUniform4iv +#define glUniform4ui wrap_glUniform4ui +#define glUniform4uiv wrap_glUniform4uiv +#define glUniformBlockBinding wrap_glUniformBlockBinding +#define glUniformHandleui64NV wrap_glUniformHandleui64NV +#define glUniformHandleui64vNV wrap_glUniformHandleui64vNV +#define glUniformMatrix2fv wrap_glUniformMatrix2fv +#define glUniformMatrix2x3fv wrap_glUniformMatrix2x3fv +#define glUniformMatrix2x3fvNV wrap_glUniformMatrix2x3fvNV +#define glUniformMatrix2x4fv wrap_glUniformMatrix2x4fv +#define glUniformMatrix2x4fvNV wrap_glUniformMatrix2x4fvNV +#define glUniformMatrix3fv wrap_glUniformMatrix3fv +#define glUniformMatrix3x2fv wrap_glUniformMatrix3x2fv +#define glUniformMatrix3x2fvNV wrap_glUniformMatrix3x2fvNV +#define glUniformMatrix3x4fv wrap_glUniformMatrix3x4fv +#define glUniformMatrix3x4fvNV wrap_glUniformMatrix3x4fvNV +#define glUniformMatrix4fv wrap_glUniformMatrix4fv +#define glUniformMatrix4x2fv wrap_glUniformMatrix4x2fv +#define glUniformMatrix4x2fvNV wrap_glUniformMatrix4x2fvNV +#define glUniformMatrix4x3fv wrap_glUniformMatrix4x3fv +#define glUniformMatrix4x3fvNV wrap_glUniformMatrix4x3fvNV +#define glUnmapBuffer wrap_glUnmapBuffer +#define glUnmapBufferOES wrap_glUnmapBufferOES +#define glUseProgram wrap_glUseProgram +#define glUseProgramStages wrap_glUseProgramStages +#define glUseProgramStagesEXT wrap_glUseProgramStagesEXT +#define glValidateProgram wrap_glValidateProgram +#define glValidateProgramPipeline wrap_glValidateProgramPipeline +#define glValidateProgramPipelineEXT wrap_glValidateProgramPipelineEXT +#define glVertexAttrib1f wrap_glVertexAttrib1f +#define glVertexAttrib1fv wrap_glVertexAttrib1fv +#define glVertexAttrib2f wrap_glVertexAttrib2f +#define glVertexAttrib2fv wrap_glVertexAttrib2fv +#define glVertexAttrib3f wrap_glVertexAttrib3f +#define glVertexAttrib3fv wrap_glVertexAttrib3fv +#define glVertexAttrib4f wrap_glVertexAttrib4f +#define glVertexAttrib4fv wrap_glVertexAttrib4fv +#define glVertexAttribBinding wrap_glVertexAttribBinding +#define glVertexAttribDivisor wrap_glVertexAttribDivisor +#define glVertexAttribDivisorANGLE wrap_glVertexAttribDivisorANGLE +#define glVertexAttribDivisorEXT wrap_glVertexAttribDivisorEXT +#define glVertexAttribDivisorNV wrap_glVertexAttribDivisorNV +#define glVertexAttribFormat wrap_glVertexAttribFormat +#define glVertexAttribI4i wrap_glVertexAttribI4i +#define glVertexAttribI4iv wrap_glVertexAttribI4iv +#define glVertexAttribI4ui wrap_glVertexAttribI4ui +#define glVertexAttribI4uiv wrap_glVertexAttribI4uiv +#define glVertexAttribIFormat wrap_glVertexAttribIFormat +#define glVertexAttribIPointer wrap_glVertexAttribIPointer +#define glVertexAttribPointer wrap_glVertexAttribPointer +#define glVertexBindingDivisor wrap_glVertexBindingDivisor +#define glVertexPointer wrap_glVertexPointer +#define glViewport wrap_glViewport +#define glViewportArrayvNV wrap_glViewportArrayvNV +#define glViewportIndexedfNV wrap_glViewportIndexedfNV +#define glViewportIndexedfvNV wrap_glViewportIndexedfvNV +#define glWaitSync wrap_glWaitSync +#define glWaitSyncAPPLE wrap_glWaitSyncAPPLE +#define glWeightPathsNV wrap_glWeightPathsNV +#define glWeightPointerOES wrap_glWeightPointerOES
\ No newline at end of file diff --git a/libs/hwui/debug/wrap_gles.h b/libs/hwui/debug/wrap_gles.h index ecd46e249969..27a29aa0a6b2 100644 --- a/libs/hwui/debug/wrap_gles.h +++ b/libs/hwui/debug/wrap_gles.h @@ -20,7 +20,16 @@ #endif #define HWUI_GLES_WRAP_ENABLED -#include "GlesDriver.h" +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <GLES3/gl3.h> +#include <GLES3/gl31.h> +#include <GLES3/gl32.h> + +// Generate stubs that route all the calls to our function table +#include "gles_redefine.h" #define GL_ENTRY(ret, api, ...) ret api(__VA_ARGS__); diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index d413083c2ebb..01c89ba2327f 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -30,6 +30,7 @@ import android.annotation.IntDef; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; +import java.io.DataInput; import java.io.EOFException; import java.io.File; import java.io.FileDescriptor; @@ -692,8 +693,8 @@ public class ExifInterface { private Object getValue(ByteOrder byteOrder) { try { - ByteOrderAwarenessDataInputStream inputStream = - new ByteOrderAwarenessDataInputStream(bytes); + ByteOrderedDataInputStream inputStream = + new ByteOrderedDataInputStream(bytes); inputStream.setByteOrder(byteOrder); switch (format) { case IFD_FORMAT_BYTE: @@ -1677,21 +1678,24 @@ public class ExifInterface { in = new BufferedInputStream(in, SIGNATURE_CHECK_SIZE); mMimeType = getMimeType((BufferedInputStream) in); + // Create byte-ordered input stream + ByteOrderedDataInputStream inputStream = new ByteOrderedDataInputStream(in); + switch (mMimeType) { case IMAGE_TYPE_JPEG: { - getJpegAttributes(in, 0, IFD_TYPE_PRIMARY); // 0 is offset + getJpegAttributes(inputStream, 0, IFD_TYPE_PRIMARY); // 0 is offset break; } case IMAGE_TYPE_RAF: { - getRafAttributes(in); + getRafAttributes(inputStream); break; } case IMAGE_TYPE_ORF: { - getOrfAttributes(in); + getOrfAttributes(inputStream); break; } case IMAGE_TYPE_RW2: { - getRw2Attributes(in); + getRw2Attributes(inputStream); break; } case IMAGE_TYPE_ARW: @@ -1702,7 +1706,7 @@ public class ExifInterface { case IMAGE_TYPE_PEF: case IMAGE_TYPE_SRW: case IMAGE_TYPE_UNKNOWN: { - getRawAttributes(in); + getRawAttributes(inputStream); break; } default: { @@ -1710,7 +1714,7 @@ public class ExifInterface { } } // Set thumbnail image offset and length - setThumbnailData(in); + setThumbnailData(inputStream); } catch (IOException e) { // Ignore exceptions in order to keep the compatibility with the old versions of // ExifInterface. @@ -2144,8 +2148,8 @@ public class ExifInterface { * http://fileformats.archiveteam.org/wiki/Olympus_ORF */ private boolean isOrfFormat(byte[] signatureCheckBytes) throws IOException { - ByteOrderAwarenessDataInputStream signatureInputStream = - new ByteOrderAwarenessDataInputStream(signatureCheckBytes); + ByteOrderedDataInputStream signatureInputStream = + new ByteOrderedDataInputStream(signatureCheckBytes); // Read byte order mExifByteOrder = readByteOrder(signatureInputStream); // Set byte order @@ -2163,8 +2167,8 @@ public class ExifInterface { * See http://lclevy.free.fr/raw/ */ private boolean isRw2Format(byte[] signatureCheckBytes) throws IOException { - ByteOrderAwarenessDataInputStream signatureInputStream = - new ByteOrderAwarenessDataInputStream(signatureCheckBytes); + ByteOrderedDataInputStream signatureInputStream = + new ByteOrderedDataInputStream(signatureCheckBytes); // Read byte order mExifByteOrder = readByteOrder(signatureInputStream); // Set byte order @@ -2187,39 +2191,36 @@ public class ExifInterface { * IFD_TYPE_THUMBNAIL for thumbnail image. * @throws IOException If the data contains invalid JPEG markers, offsets, or length values. */ - private void getJpegAttributes(InputStream inputStream, int jpegOffset, int imageType) + private void getJpegAttributes(ByteOrderedDataInputStream in, int jpegOffset, int imageType) throws IOException { // See JPEG File Interchange Format Specification, "JFIF Specification" if (DEBUG) { - Log.d(TAG, "getJpegAttributes starting with: " + inputStream); + Log.d(TAG, "getJpegAttributes starting with: " + in); } - DataInputStream dataInputStream = new DataInputStream(inputStream); - // Mark current position to reset after retrieving data - dataInputStream.mark(dataInputStream.available()); + // JPEG uses Big Endian by default. See https://people.cs.umass.edu/~verts/cs32/endian.html + in.setByteOrder(ByteOrder.BIG_ENDIAN); // Skip to JPEG data - if (dataInputStream.skip(jpegOffset) != jpegOffset) { - throw new IOException("Invalid JPEG offset"); - } + in.seek(jpegOffset); int bytesRead = jpegOffset; byte marker; - if ((marker = dataInputStream.readByte()) != MARKER) { + if ((marker = in.readByte()) != MARKER) { throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); } ++bytesRead; - if (dataInputStream.readByte() != MARKER_SOI) { + if (in.readByte() != MARKER_SOI) { throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); } ++bytesRead; while (true) { - marker = dataInputStream.readByte(); + marker = in.readByte(); if (marker != MARKER) { throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff)); } ++bytesRead; - marker = dataInputStream.readByte(); + marker = in.readByte(); if (DEBUG) { Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff)); } @@ -2230,7 +2231,7 @@ public class ExifInterface { if (marker == MARKER_EOI || marker == MARKER_SOS) { break; } - int length = dataInputStream.readUnsignedShort() - 2; + int length = in.readUnsignedShort() - 2; bytesRead += 2; if (DEBUG) { Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: " @@ -2249,7 +2250,7 @@ public class ExifInterface { break; } byte[] identifier = new byte[6]; - if (inputStream.read(identifier) != 6) { + if (in.read(identifier) != 6) { throw new IOException("Invalid exif"); } bytesRead += 6; @@ -2268,7 +2269,7 @@ public class ExifInterface { mExifOffset = bytesRead; byte[] bytes = new byte[length]; - if (dataInputStream.read(bytes) != length) { + if (in.read(bytes) != length) { throw new IOException("Invalid exif"); } bytesRead += length; @@ -2280,7 +2281,7 @@ public class ExifInterface { case MARKER_COM: { byte[] bytes = new byte[length]; - if (dataInputStream.read(bytes) != length) { + if (in.read(bytes) != length) { throw new IOException("Invalid exif"); } length = 0; @@ -2304,13 +2305,13 @@ public class ExifInterface { case MARKER_SOF13: case MARKER_SOF14: case MARKER_SOF15: { - if (dataInputStream.skipBytes(1) != 1) { + if (in.skipBytes(1) != 1) { throw new IOException("Invalid SOFx"); } mAttributes[imageType].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong( - dataInputStream.readUnsignedShort(), mExifByteOrder)); + in.readUnsignedShort(), mExifByteOrder)); mAttributes[imageType].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong( - dataInputStream.readUnsignedShort(), mExifByteOrder)); + in.readUnsignedShort(), mExifByteOrder)); length -= 5; break; } @@ -2322,30 +2323,21 @@ public class ExifInterface { if (length < 0) { throw new IOException("Invalid length"); } - if (dataInputStream.skipBytes(length) != length) { + if (in.skipBytes(length) != length) { throw new IOException("Invalid JPEG segment"); } bytesRead += length; } - // Reset dataInputStream to marked position - dataInputStream.reset(); + // Restore original byte order + in.setByteOrder(mExifByteOrder); } - private void getRawAttributes(InputStream in) throws IOException { - int totalBytes = in.available(); - byte[] exifBytes = new byte[totalBytes]; - in.mark(in.available()); - in.read(exifBytes); - in.reset(); - - ByteOrderAwarenessDataInputStream dataInputStream = - new ByteOrderAwarenessDataInputStream(exifBytes); - + private void getRawAttributes(ByteOrderedDataInputStream in) throws IOException { // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1. - parseTiffHeaders(dataInputStream, exifBytes.length); + parseTiffHeaders(in, in.available()); // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6. - readImageFileDirectory(dataInputStream, IFD_TYPE_PRIMARY); + readImageFileDirectory(in, IFD_TYPE_PRIMARY); // Update ImageLength/Width tags for all image data. updateImageSizeValues(in, IFD_TYPE_PRIMARY); @@ -2362,8 +2354,8 @@ public class ExifInterface { (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE); if (makerNoteAttribute != null) { // Create an ordered DataInputStream for MakerNote - ByteOrderAwarenessDataInputStream makerNoteDataInputStream = - new ByteOrderAwarenessDataInputStream(makerNoteAttribute.bytes); + ByteOrderedDataInputStream makerNoteDataInputStream = + new ByteOrderedDataInputStream(makerNoteAttribute.bytes); makerNoteDataInputStream.setByteOrder(mExifByteOrder); // Seek to MakerNote data @@ -2391,45 +2383,27 @@ public class ExifInterface { * then parses the CFA metadata to retrieve the primary image length/width values. * For data format details, see http://fileformats.archiveteam.org/wiki/Fujifilm_RAF */ - private void getRafAttributes(InputStream in) throws IOException { + private void getRafAttributes(ByteOrderedDataInputStream in) throws IOException { // Retrieve offset & length values - in.mark(RAF_INFO_SIZE); - in.skip(RAF_OFFSET_TO_JPEG_IMAGE_OFFSET); + in.skipBytes(RAF_OFFSET_TO_JPEG_IMAGE_OFFSET); byte[] jpegOffsetBytes = new byte[4]; byte[] cfaHeaderOffsetBytes = new byte[4]; - byte[] cfaHeaderLengthBytes = new byte[4]; in.read(jpegOffsetBytes); // Skip JPEG length value since it is not needed - in.skip(RAF_JPEG_LENGTH_VALUE_SIZE); + in.skipBytes(RAF_JPEG_LENGTH_VALUE_SIZE); in.read(cfaHeaderOffsetBytes); - in.read(cfaHeaderLengthBytes); int rafJpegOffset = ByteBuffer.wrap(jpegOffsetBytes).getInt(); int rafCfaHeaderOffset = ByteBuffer.wrap(cfaHeaderOffsetBytes).getInt(); - int rafCfaHeaderLength = ByteBuffer.wrap(cfaHeaderLengthBytes).getInt(); - in.reset(); // Retrieve JPEG image metadata getJpegAttributes(in, rafJpegOffset, IFD_TYPE_PREVIEW); // Skip to CFA header offset. - // A while loop is used because the skip method may not be able to skip the requested amount - // at once because the size of the buffer may be restricted. - in.mark(rafCfaHeaderOffset + rafCfaHeaderLength); - int totalSkip = rafCfaHeaderOffset; - while (totalSkip > 0) { - long skipped = in.skip(totalSkip); - totalSkip -= skipped; - } + in.seek(rafCfaHeaderOffset); // Retrieve primary image length/width values, if TAG_RAF_IMAGE_SIZE exists - byte[] exifBytes = new byte[rafCfaHeaderLength]; - if (in.read(exifBytes) != rafCfaHeaderLength) { - throw new EOFException(); - } - in.reset(); - ByteOrderAwarenessDataInputStream dataInputStream = - new ByteOrderAwarenessDataInputStream(exifBytes); - int numberOfDirectoryEntry = dataInputStream.readInt(); + in.setByteOrder(ByteOrder.BIG_ENDIAN); + int numberOfDirectoryEntry = in.readInt(); if (DEBUG) { Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry); } @@ -2437,11 +2411,11 @@ public class ExifInterface { // find and retrieve image size information tags, while skipping others. // See piex.cc RafGetDimension() for (int i = 0; i < numberOfDirectoryEntry; ++i) { - int tagNumber = dataInputStream.readUnsignedShort(); - int numberOfBytes = dataInputStream.readUnsignedShort(); + int tagNumber = in.readUnsignedShort(); + int numberOfBytes = in.readUnsignedShort(); if (tagNumber == TAG_RAF_IMAGE_SIZE.number) { - int imageLength = dataInputStream.readShort(); - int imageWidth = dataInputStream.readShort(); + int imageLength = in.readShort(); + int imageWidth = in.readShort(); ExifAttribute imageLengthAttribute = ExifAttribute.createUShort(imageLength, mExifByteOrder); ExifAttribute imageWidthAttribute = @@ -2453,7 +2427,7 @@ public class ExifInterface { } return; } - dataInputStream.skip(numberOfBytes); + in.skipBytes(numberOfBytes); } } @@ -2467,7 +2441,7 @@ public class ExifInterface { * http://fileformats.archiveteam.org/wiki/Olympus_ORF * https://libopenraw.freedesktop.org/wiki/Olympus_ORF */ - private void getOrfAttributes(InputStream in) throws IOException { + private void getOrfAttributes(ByteOrderedDataInputStream in) throws IOException { // Retrieve primary image data // Other Exif data will be located in the Makernote. getRawAttributes(in); @@ -2479,8 +2453,8 @@ public class ExifInterface { (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE); if (makerNoteAttribute != null) { // Create an ordered DataInputStream for MakerNote - ByteOrderAwarenessDataInputStream makerNoteDataInputStream = - new ByteOrderAwarenessDataInputStream(makerNoteAttribute.bytes); + ByteOrderedDataInputStream makerNoteDataInputStream = + new ByteOrderedDataInputStream(makerNoteAttribute.bytes); makerNoteDataInputStream.setByteOrder(mExifByteOrder); // There are two types of headers for Olympus MakerNotes @@ -2546,7 +2520,7 @@ public class ExifInterface { // RW2 contains the primary image data in IFD0 and the preview and/or thumbnail image data in // the JpgFromRaw tag // See https://libopenraw.freedesktop.org/wiki/Panasonic_RAW/ and piex.cc Rw2GetPreviewData() - private void getRw2Attributes(InputStream in) throws IOException { + private void getRw2Attributes(ByteOrderedDataInputStream in) throws IOException { // Retrieve primary image data getRawAttributes(in); @@ -2577,8 +2551,8 @@ public class ExifInterface { + ", outputStream: " + outputStream + ")"); } DataInputStream dataInputStream = new DataInputStream(inputStream); - ByteOrderAwarenessDataOutputStream dataOutputStream = - new ByteOrderAwarenessDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN); + ByteOrderedDataOutputStream dataOutputStream = + new ByteOrderedDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN); if (dataInputStream.readByte() != MARKER) { throw new IOException("Invalid marker"); } @@ -2614,7 +2588,7 @@ public class ExifInterface { } if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { // Skip the original EXIF APP1 segment. - if (dataInputStream.skip(length - 6) != length - 6) { + if (dataInputStream.skipBytes(length - 6) != length - 6) { throw new IOException("Invalid length"); } break; @@ -2668,8 +2642,8 @@ public class ExifInterface { // Reads the given EXIF byte area and save its tag data into attributes. private void readExifSegment(byte[] exifBytes, int imageType) throws IOException { - ByteOrderAwarenessDataInputStream dataInputStream = - new ByteOrderAwarenessDataInputStream(exifBytes); + ByteOrderedDataInputStream dataInputStream = + new ByteOrderedDataInputStream(exifBytes); // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1. parseTiffHeaders(dataInputStream, exifBytes.length); @@ -2705,7 +2679,7 @@ public class ExifInterface { } } - private ByteOrder readByteOrder(ByteOrderAwarenessDataInputStream dataInputStream) + private ByteOrder readByteOrder(ByteOrderedDataInputStream dataInputStream) throws IOException { // Read byte order. short byteOrder = dataInputStream.readShort(); @@ -2725,7 +2699,7 @@ public class ExifInterface { } } - private void parseTiffHeaders(ByteOrderAwarenessDataInputStream dataInputStream, + private void parseTiffHeaders(ByteOrderedDataInputStream dataInputStream, int exifBytesLength) throws IOException { // Read byte order mExifByteOrder = readByteOrder(dataInputStream); @@ -2745,22 +2719,22 @@ public class ExifInterface { } firstIfdOffset -= 8; if (firstIfdOffset > 0) { - if (dataInputStream.skip(firstIfdOffset) != firstIfdOffset) { + if (dataInputStream.skipBytes(firstIfdOffset) != firstIfdOffset) { throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset); } } } // Reads image file directory, which is a tag group in EXIF. - private void readImageFileDirectory(ByteOrderAwarenessDataInputStream dataInputStream, + private void readImageFileDirectory(ByteOrderedDataInputStream dataInputStream, @IfdType int ifdType) throws IOException { - if (dataInputStream.peek() + 2 > dataInputStream.mLength) { + if (dataInputStream.mPosition + 2 > dataInputStream.mLength) { // Return if there is no data from the offset. return; } // See TIFF 6.0 Section 2: TIFF Structure, Figure 1. short numberOfDirectoryEntry = dataInputStream.readShort(); - if (dataInputStream.peek() + 12 * numberOfDirectoryEntry > dataInputStream.mLength) { + if (dataInputStream.mPosition + 12 * numberOfDirectoryEntry > dataInputStream.mLength) { // Return if the size of entries is too big. return; } @@ -2789,10 +2763,12 @@ public class ExifInterface { if (tag == null || dataFormat <= 0 || dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) { // Skip if the parsed tag number is not defined or invalid data format. - if (tag == null) { - Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber); - } else { - Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat); + if (DEBUG) { + if (tag == null) { + Log.w(TAG, "Skip tag entry since tag number is not defined: " + tagNumber); + } else { + Log.w(TAG, "Skip tag entry since data format is invalid: " + dataFormat); + } } dataInputStream.seek(nextEntryOffset); continue; @@ -2908,7 +2884,7 @@ public class ExifInterface { if (((tag.name == TAG_MAKE || tag.name == TAG_MODEL) && attribute.getStringValue(mExifByteOrder).contains(PEF_SIGNATURE)) || (tag.name == TAG_COMPRESSION - && attribute.getIntValue(mExifByteOrder) == 65535)) { + && attribute.getIntValue(mExifByteOrder) == 65535)) { mMimeType = IMAGE_TYPE_PEF; } @@ -2943,7 +2919,8 @@ public class ExifInterface { * to locate SOF(Start of Frame) marker and update the image length & width values. * See JEITA CP-3451C Table 5 and Section 4.8.1. B. */ - private void retrieveJpegImageSize(InputStream in, int imageType) throws IOException { + private void retrieveJpegImageSize(ByteOrderedDataInputStream in, int imageType) + throws IOException { // Check if image already has IMAGE_LENGTH & IMAGE_WIDTH values ExifAttribute imageLengthAttribute = (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_LENGTH); @@ -2965,7 +2942,7 @@ public class ExifInterface { } // Sets thumbnail offset & length attributes based on JpegInterchangeFormat or StripOffsets tags - private void setThumbnailData(InputStream in) throws IOException { + private void setThumbnailData(ByteOrderedDataInputStream in) throws IOException { HashMap thumbnailData = mAttributes[IFD_TYPE_THUMBNAIL]; ExifAttribute compressionAttribute = @@ -2994,7 +2971,8 @@ public class ExifInterface { // Check JpegInterchangeFormat(JFIF) tags to retrieve thumbnail offset & length values // and reads the corresponding bytes if stream does not support seek function - private void handleThumbnailFromJfif(InputStream in, HashMap thumbnailData) throws IOException { + private void handleThumbnailFromJfif(ByteOrderedDataInputStream in, HashMap thumbnailData) + throws IOException { ExifAttribute jpegInterchangeFormatAttribute = (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT); ExifAttribute jpegInterchangeFormatLengthAttribute = @@ -3025,8 +3003,8 @@ public class ExifInterface { && mSeekableFileDescriptor == null) { // Save the thumbnail in memory if the input doesn't support reading again. byte[] thumbnailBytes = new byte[thumbnailLength]; - in.skip(thumbnailOffset); - in.read(thumbnailBytes); + in.seek(thumbnailOffset); + in.readFully(thumbnailBytes); mThumbnailBytes = thumbnailBytes; } } @@ -3034,7 +3012,7 @@ public class ExifInterface { } // Check StripOffsets & StripByteCounts tags to retrieve thumbnail offset & length values - private void handleThumbnailFromStrips(InputStream in, HashMap thumbnailData) + private void handleThumbnailFromStrips(ByteOrderedDataInputStream in, HashMap thumbnailData) throws IOException { ExifAttribute stripOffsetsAttribute = (ExifAttribute) thumbnailData.get(TAG_STRIP_OFFSETS); @@ -3062,7 +3040,7 @@ public class ExifInterface { if (skipBytes < 0) { Log.d(TAG, "Invalid strip offset value"); } - in.skip(skipBytes); + in.seek(skipBytes); bytesRead += skipBytes; // Read strip bytes @@ -3140,6 +3118,18 @@ public class ExifInterface { swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_THUMBNAIL); swapBasedOnImageSize(IFD_TYPE_PREVIEW, IFD_TYPE_THUMBNAIL); + // Check if image has PixelXDimension/PixelYDimension tags, which contain valid image + // sizes, excluding padding at the right end or bottom end of the image to make sure that + // the values are multiples of 64. See JEITA CP-3451C Table 5 and Section 4.8.1. B. + ExifAttribute pixelXDimAttribute = + (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_X_DIMENSION); + ExifAttribute pixelYDimAttribute = + (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_Y_DIMENSION); + if (pixelXDimAttribute != null && pixelYDimAttribute != null) { + mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, pixelXDimAttribute); + mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, pixelYDimAttribute); + } + // Check whether thumbnail image exists and whether preview image satisfies the thumbnail // image requirements if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) { @@ -3162,17 +3152,11 @@ public class ExifInterface { * This method corrects those tag values by checking first the values of TAG_DEFAULT_CROP_SIZE * See DNG Specification 1.4.0.0. Section 4. (DefaultCropSize) * - * If image is JPEG compressed, PixelXDimension/PixelYDimension tags are used for size info. - * However, an image may have padding at the right end or bottom end of the image to make sure - * that the values are multiples of 64. If so, the increased value will be saved in the - * SOF(Start of Frame). In order to assure that valid image size values are stored, this method - * checks TAG_PIXEL_X_DIMENSION & TAG_PIXEL_Y_DIMENSION and updates values if necessary. - * See JEITA CP-3451C Table 5 and Section 4.8.1. B. - * * If image is a RW2 file, valid image sizes are stored in SensorBorder tags. * See tiff_parser.cc GetFullDimension32() * */ - private void updateImageSizeValues(InputStream in, int imageType) throws IOException { + private void updateImageSizeValues(ByteOrderedDataInputStream in, int imageType) + throws IOException { // Uncompressed image valid image size values ExifAttribute defaultCropSizeAttribute = (ExifAttribute) mAttributes[imageType].get(TAG_DEFAULT_CROP_SIZE); @@ -3224,33 +3208,12 @@ public class ExifInterface { mAttributes[imageType].put(TAG_IMAGE_WIDTH, imageWidthAttribute); } } else { - // Update for JPEG image - ExifAttribute newSubfileTypeAttribute = - (ExifAttribute) mAttributes[imageType].get(TAG_NEW_SUBFILE_TYPE); - - if (newSubfileTypeAttribute != null) { - int newSubfileTypeValue = newSubfileTypeAttribute.getIntValue(mExifByteOrder); - - if (newSubfileTypeValue == ORIGINAL_RESOLUTION_IMAGE) { - // Update only for the primary image (OriginalResolutionImage) - ExifAttribute pixelXDimAttribute = - (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_X_DIMENSION); - ExifAttribute pixelYDimAttribute = - (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_Y_DIMENSION); - - if (pixelXDimAttribute != null && pixelYDimAttribute != null) { - mAttributes[imageType].put(TAG_IMAGE_WIDTH, pixelXDimAttribute); - mAttributes[imageType].put(TAG_IMAGE_LENGTH, pixelYDimAttribute); - return; - } - } - } retrieveJpegImageSize(in, imageType); } } // Writes an Exif segment into the given output stream. - private int writeExifSegment(ByteOrderAwarenessDataOutputStream dataOutputStream, + private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream, int exifOffsetFromBeginning) throws IOException { // The following variables are for calculating each IFD tag group size in bytes. int[] ifdOffsets = new int[EXIF_TAGS.length]; @@ -3414,7 +3377,7 @@ public class ExifInterface { // Write thumbnail if (mHasThumbnail) { - dataOutputStream.write(getThumbnail()); + dataOutputStream.write(getThumbnailBytes()); } // Reset the byte order to big endian in order to write remaining parts of the JPEG file. @@ -3507,18 +3470,26 @@ public class ExifInterface { // An input stream to parse EXIF data area, which can be written in either little or big endian // order. - private static class ByteOrderAwarenessDataInputStream extends ByteArrayInputStream { + private static class ByteOrderedDataInputStream extends InputStream implements DataInput { private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN; private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN; + private DataInputStream mDataInputStream; + private InputStream mInputStream; private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN; - private final long mLength; - private long mPosition; + private final int mLength; + private int mPosition; + + public ByteOrderedDataInputStream(InputStream in) throws IOException { + mInputStream = in; + mDataInputStream = new DataInputStream(in); + mLength = mDataInputStream.available(); + mPosition = 0; + mDataInputStream.mark(mLength); + } - public ByteOrderAwarenessDataInputStream(byte[] bytes) { - super(bytes); - mLength = bytes.length; - mPosition = 0L; + public ByteOrderedDataInputStream(byte[] bytes) throws IOException { + this(new ByteArrayInputStream(bytes)); } public void setByteOrder(ByteOrder byteOrder) { @@ -3527,49 +3498,106 @@ public class ExifInterface { public void seek(long byteCount) throws IOException { if (mPosition > byteCount) { - mPosition = 0L; - reset(); + mPosition = 0; + mDataInputStream.reset(); + mDataInputStream.mark(mLength); } else { byteCount -= mPosition; } - if (skip(byteCount) != byteCount) { + + if (skipBytes((int) byteCount) != (int) byteCount) { throw new IOException("Couldn't seek up to the byteCount"); } } - public long peek() { + public int peek() { return mPosition; } + @Override + public int available() throws IOException { + return mDataInputStream.available(); + } + + @Override + public int read() throws IOException { + ++mPosition; + return mDataInputStream.read(); + } + + @Override + public int readUnsignedByte() throws IOException { + ++mPosition; + return mDataInputStream.readUnsignedByte(); + } + + @Override + public String readLine() throws IOException { + Log.d(TAG, "Currently unsupported"); + return null; + } + + @Override + public boolean readBoolean() throws IOException { + ++mPosition; + return mDataInputStream.readBoolean(); + } + + @Override + public char readChar() throws IOException { + mPosition += 2; + return mDataInputStream.readChar(); + } + + @Override + public String readUTF() throws IOException { + mPosition += 2; + return mDataInputStream.readUTF(); + } + + @Override + public void readFully(byte[] buffer, int offset, int length) throws IOException { + mPosition += length; + if (mPosition > mLength) { + throw new EOFException(); + } + if (mDataInputStream.read(buffer, offset, length) != length) { + throw new IOException("Couldn't read up to the length of buffer"); + } + } + + @Override public void readFully(byte[] buffer) throws IOException { mPosition += buffer.length; if (mPosition > mLength) { throw new EOFException(); } - if (super.read(buffer, 0, buffer.length) != buffer.length) { + if (mDataInputStream.read(buffer, 0, buffer.length) != buffer.length) { throw new IOException("Couldn't read up to the length of buffer"); } } + @Override public byte readByte() throws IOException { ++mPosition; if (mPosition > mLength) { throw new EOFException(); } - int ch = super.read(); + int ch = mDataInputStream.read(); if (ch < 0) { throw new EOFException(); } return (byte) ch; } + @Override public short readShort() throws IOException { mPosition += 2; if (mPosition > mLength) { throw new EOFException(); } - int ch1 = super.read(); - int ch2 = super.read(); + int ch1 = mDataInputStream.read(); + int ch2 = mDataInputStream.read(); if ((ch1 | ch2) < 0) { throw new EOFException(); } @@ -3581,15 +3609,16 @@ public class ExifInterface { throw new IOException("Invalid byte order: " + mByteOrder); } + @Override public int readInt() throws IOException { mPosition += 4; if (mPosition > mLength) { throw new EOFException(); } - int ch1 = super.read(); - int ch2 = super.read(); - int ch3 = super.read(); - int ch4 = super.read(); + int ch1 = mDataInputStream.read(); + int ch2 = mDataInputStream.read(); + int ch3 = mDataInputStream.read(); + int ch4 = mDataInputStream.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) { throw new EOFException(); } @@ -3602,8 +3631,12 @@ public class ExifInterface { } @Override - public long skip(long byteCount) { - long skipped = super.skip(Math.min(byteCount, mLength - mPosition)); + public int skipBytes(int byteCount) throws IOException { + int totalSkip = Math.min(byteCount, mLength - mPosition); + int skipped = 0; + while (skipped < totalSkip) { + skipped += mDataInputStream.skipBytes(totalSkip - skipped); + } mPosition += skipped; return skipped; } @@ -3613,8 +3646,8 @@ public class ExifInterface { if (mPosition > mLength) { throw new EOFException(); } - int ch1 = super.read(); - int ch2 = super.read(); + int ch1 = mDataInputStream.read(); + int ch2 = mDataInputStream.read(); if ((ch1 | ch2) < 0) { throw new EOFException(); } @@ -3630,19 +3663,20 @@ public class ExifInterface { return readInt() & 0xffffffffL; } + @Override public long readLong() throws IOException { mPosition += 8; if (mPosition > mLength) { throw new EOFException(); } - int ch1 = super.read(); - int ch2 = super.read(); - int ch3 = super.read(); - int ch4 = super.read(); - int ch5 = super.read(); - int ch6 = super.read(); - int ch7 = super.read(); - int ch8 = super.read(); + int ch1 = mDataInputStream.read(); + int ch2 = mDataInputStream.read(); + int ch3 = mDataInputStream.read(); + int ch4 = mDataInputStream.read(); + int ch5 = mDataInputStream.read(); + int ch6 = mDataInputStream.read(); + int ch7 = mDataInputStream.read(); + int ch8 = mDataInputStream.read(); if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) { throw new EOFException(); } @@ -3658,10 +3692,12 @@ public class ExifInterface { throw new IOException("Invalid byte order: " + mByteOrder); } + @Override public float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); } + @Override public double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); } @@ -3669,11 +3705,11 @@ public class ExifInterface { // An output stream to write EXIF data area, which can be written in either little or big endian // order. - private static class ByteOrderAwarenessDataOutputStream extends FilterOutputStream { + private static class ByteOrderedDataOutputStream extends FilterOutputStream { private final OutputStream mOutputStream; private ByteOrder mByteOrder; - public ByteOrderAwarenessDataOutputStream(OutputStream out, ByteOrder byteOrder) { + public ByteOrderedDataOutputStream(OutputStream out, ByteOrder byteOrder) { super(out); mOutputStream = out; mByteOrder = byteOrder; diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 101facd4909b..5b4443bf15c5 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -66,7 +66,7 @@ public class MediaRouter { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); static class Static implements DisplayManager.DisplayListener { - final Context mAppContext; + final String mPackageName; final Resources mResources; final IAudioService mAudioService; final DisplayManager mDisplayService; @@ -110,8 +110,8 @@ public class MediaRouter { }; Static(Context appContext) { - mAppContext = appContext; - mResources = Resources.getSystem(); + mPackageName = appContext.getPackageName(); + mResources = appContext.getResources(); mHandler = new Handler(appContext.getMainLooper()); IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); @@ -357,8 +357,7 @@ public class MediaRouter { try { Client client = new Client(); - mMediaRouterService.registerClientAsUser(client, - mAppContext.getPackageName(), userId); + mMediaRouterService.registerClientAsUser(client, mPackageName, userId); mClient = client; } catch (RemoteException ex) { Log.e(TAG, "Unable to register media router client.", ex); @@ -1627,7 +1626,7 @@ public class MediaRouter { CharSequence getName(Resources res) { if (mNameResId != 0) { - return mName = res.getText(mNameResId); + return res.getText(mNameResId); } return mName; } @@ -2079,6 +2078,7 @@ public class MediaRouter { * @param name Name to display to the user to describe this route */ public void setName(CharSequence name) { + mNameResId = 0; mName = name; routeUpdated(); } @@ -2275,7 +2275,7 @@ public class MediaRouter { private void configureSessionVolume() { if (mRcc == null) { if (DEBUG) { - Log.d(TAG, "No Rcc to configure volume for route " + mName); + Log.d(TAG, "No Rcc to configure volume for route " + getName()); } return; } @@ -2590,8 +2590,10 @@ public class MediaRouter { for (int i = 0; i < count; i++) { final RouteInfo info = mRoutes.get(i); // TODO: There's probably a much more correct way to localize this. - if (i > 0) sb.append(", "); - sb.append(info.mName); + if (i > 0) { + sb.append(", "); + } + sb.append(info.getName()); } mName = sb.toString(); mUpdateName = false; @@ -2716,7 +2718,7 @@ public class MediaRouter { @Override public String toString() { - return "RouteCategory{ name=" + mName + " types=" + typesToString(mTypes) + + return "RouteCategory{ name=" + getName() + " types=" + typesToString(mTypes) + " groupable=" + mGroupable + " }"; } } diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk index 7e438a12f31b..5d737308c8cf 100644 --- a/media/tests/MediaFrameworkTest/Android.mk +++ b/media/tests/MediaFrameworkTest/Android.mk @@ -9,7 +9,7 @@ LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_JAVA_LANGUAGE_VERSION := 1.8 -LOCAL_STATIC_JAVA_LIBRARIES := easymocklib \ +LOCAL_STATIC_JAVA_LIBRARIES := \ mockito-target \ android-support-test \ android-ex-camera2 diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java index 06efa906af04..86c2284e86ff 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java @@ -16,6 +16,14 @@ package com.android.mediaframeworktest.unit; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; + import android.content.ContentProviderClient; import android.content.ContentValues; import android.content.IContentProvider; @@ -28,14 +36,16 @@ import android.provider.MediaStore.Video; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.SmallTest; -import org.easymock.EasyMock; -import org.easymock.IArgumentMatcher; +import org.hamcrest.Description; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; public class MediaInserterTest extends InstrumentationTestCase { private MediaInserter mMediaInserter; private static final int TEST_BUFFER_SIZE = 10; - private IContentProvider mMockProvider; + private @Mock IContentProvider mMockProvider; private String mPackageName; private int mFilesCounter; @@ -49,8 +59,8 @@ public class MediaInserterTest extends InstrumentationTestCase { private static final Uri sImagesUri = Images.Media.getContentUri(sVolumeName); private static final Uri sFilesUri = Files.getContentUri(sVolumeName); - private static class MediaUriMatcher implements IArgumentMatcher { - private Uri mUri; + private static class MediaUriMatcher extends ArgumentMatcher<Uri> { + private final Uri mUri; private MediaUriMatcher(Uri uri) { mUri = uri; @@ -63,25 +73,30 @@ public class MediaInserterTest extends InstrumentationTestCase { } Uri actualUri = (Uri) argument; - if (actualUri == mUri) return true; + if (actualUri == mUri) + return true; return false; } @Override - public void appendTo(StringBuffer buffer) { - buffer.append("expected a TableUri '").append(mUri).append("'"); + public void describeTo(Description description) { + description + .appendText("expected a TableUri '") + .appendText(mUri.toString()) + .appendText("'"); } - private static Uri expectMediaUri(Uri in) { - EasyMock.reportMatcher(new MediaUriMatcher(in)); - return null; - } + } + + private static Uri eqUri(Uri in) { + return argThat(new MediaUriMatcher(in)); } @Override protected void setUp() throws Exception { super.setUp(); - mMockProvider = EasyMock.createMock(IContentProvider.class); + MockitoAnnotations.initMocks(this); + final ContentProviderClient client = new ContentProviderClient( getInstrumentation().getContext().getContentResolver(), mMockProvider, true); mMediaInserter = new MediaInserter(client, TEST_BUFFER_SIZE); @@ -134,61 +149,46 @@ public class MediaInserterTest extends InstrumentationTestCase { @SmallTest public void testInsertContentsLessThanBufferSize() throws Exception { - EasyMock.replay(mMockProvider); - fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4); fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3); fillBuffer(sVideoUri, TEST_BUFFER_SIZE - 2); fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1); - EasyMock.verify(mMockProvider); + verify(mMockProvider, never()).bulkInsert(eq(mPackageName), any(), any()); } @SmallTest public void testInsertContentsEqualToBufferSize() throws Exception { - EasyMock.expect(mMockProvider.bulkInsert(mPackageName, - (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1); - EasyMock.expectLastCall().times(4); - EasyMock.replay(mMockProvider); + when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1); fillBuffer(sFilesUri, TEST_BUFFER_SIZE); fillBuffer(sAudioUri, TEST_BUFFER_SIZE); fillBuffer(sVideoUri, TEST_BUFFER_SIZE); fillBuffer(sImagesUri, TEST_BUFFER_SIZE); - EasyMock.verify(mMockProvider); + verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), any(), any()); } @SmallTest public void testInsertContentsMoreThanBufferSize() throws Exception { - EasyMock.expect(mMockProvider.bulkInsert(mPackageName, - (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1); - EasyMock.expectLastCall().times(4); - EasyMock.replay(mMockProvider); + when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1); fillBuffer(sFilesUri, TEST_BUFFER_SIZE + 1); fillBuffer(sAudioUri, TEST_BUFFER_SIZE + 2); fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3); fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4); - EasyMock.verify(mMockProvider); + verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), any(), any()); } @SmallTest public void testFlushAllWithEmptyContents() throws Exception { - EasyMock.replay(mMockProvider); - mMediaInserter.flushAll(); - - EasyMock.verify(mMockProvider); } @SmallTest public void testFlushAllWithSomeContents() throws Exception { - EasyMock.expect(mMockProvider.bulkInsert(mPackageName, - (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1); - EasyMock.expectLastCall().times(4); - EasyMock.replay(mMockProvider); + when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1); fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4); fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3); @@ -196,15 +196,12 @@ public class MediaInserterTest extends InstrumentationTestCase { fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1); mMediaInserter.flushAll(); - EasyMock.verify(mMockProvider); + verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), any(), any()); } @SmallTest public void testInsertContentsAfterFlushAll() throws Exception { - EasyMock.expect(mMockProvider.bulkInsert(mPackageName, - (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1); - EasyMock.expectLastCall().times(8); - EasyMock.replay(mMockProvider); + when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1); fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4); fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3); @@ -217,28 +214,15 @@ public class MediaInserterTest extends InstrumentationTestCase { fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3); fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4); - EasyMock.verify(mMockProvider); + verify(mMockProvider, times(8)).bulkInsert(eq(mPackageName), any(), any()); } @SmallTest public void testInsertContentsWithDifferentSizePerContentType() throws Exception { - EasyMock.expect(mMockProvider.bulkInsert(mPackageName, - MediaUriMatcher.expectMediaUri(sFilesUri), - (ContentValues[]) EasyMock.anyObject())).andReturn(1); - EasyMock.expectLastCall().times(1); - EasyMock.expect(mMockProvider.bulkInsert(mPackageName, - MediaUriMatcher.expectMediaUri(sAudioUri), - (ContentValues[]) EasyMock.anyObject())).andReturn(1); - EasyMock.expectLastCall().times(2); - EasyMock.expect(mMockProvider.bulkInsert(mPackageName, - MediaUriMatcher.expectMediaUri(sVideoUri), - (ContentValues[]) EasyMock.anyObject())).andReturn(1); - EasyMock.expectLastCall().times(3); - EasyMock.expect(mMockProvider.bulkInsert(mPackageName, - MediaUriMatcher.expectMediaUri(sImagesUri), - (ContentValues[]) EasyMock.anyObject())).andReturn(1); - EasyMock.expectLastCall().times(4); - EasyMock.replay(mMockProvider); + when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sFilesUri), any())).thenReturn(1); + when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sAudioUri), any())).thenReturn(1); + when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sVideoUri), any())).thenReturn(1); + when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sImagesUri), any())).thenReturn(1); for (int i = 0; i < TEST_BUFFER_SIZE; ++i) { fillBuffer(sFilesUri, 1); @@ -247,6 +231,9 @@ public class MediaInserterTest extends InstrumentationTestCase { fillBuffer(sImagesUri, 4); } - EasyMock.verify(mMockProvider); + verify(mMockProvider, times(1)).bulkInsert(eq(mPackageName), eqUri(sFilesUri), any()); + verify(mMockProvider, times(2)).bulkInsert(eq(mPackageName), eqUri(sAudioUri), any()); + verify(mMockProvider, times(3)).bulkInsert(eq(mPackageName), eqUri(sVideoUri), any()); + verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eqUri(sImagesUri), any()); } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index 2c6137263bac..0566177e1880 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -178,6 +178,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private boolean mBouncer; private boolean mBootCompleted; private boolean mUserUnlocked; + private boolean mHasLockscreenWallpaper; // Device provisioning state private boolean mDeviceProvisioned; @@ -1173,6 +1174,30 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } /** + * Update the state whether Keyguard currently has a lockscreen wallpaper. + * + * @param hasLockscreenWallpaper Whether Keyguard has a lockscreen wallpaper. + */ + public void setHasLockscreenWallpaper(boolean hasLockscreenWallpaper) { + if (hasLockscreenWallpaper != mHasLockscreenWallpaper) { + mHasLockscreenWallpaper = hasLockscreenWallpaper; + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onHasLockscreenWallpaperChanged(hasLockscreenWallpaper); + } + } + } + } + + /** + * @return Whether Keyguard has a lockscreen wallpaper. + */ + public boolean hasLockscreenWallpaper() { + return mHasLockscreenWallpaper; + } + + /** * Handle {@link #MSG_DPM_STATE_CHANGED} */ protected void handleDevicePolicyManagerStateChanged() { diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index bd6c51c3b89c..4a2d356b6ddd 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -240,4 +240,9 @@ public class KeyguardUpdateMonitorCallback { * has changed. */ public void onStrongAuthStateChanged(int userId) { } + + /** + * Called when the state whether we have a lockscreen wallpaper has changed. + */ + public void onHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper) { } } diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java index cce619e67d19..4950af3e14e0 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java @@ -873,7 +873,7 @@ class MtpDatabase { } private static int getRootFlags(int[] operationsSupported) { - int rootFlag = Root.FLAG_SUPPORTS_IS_CHILD; + int rootFlag = Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_LOCAL_ONLY; if (MtpDeviceRecord.isWritingSupported(operationsSupported)) { rootFlag |= Root.FLAG_SUPPORTS_CREATE; } diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java index 031090f1d76a..7fe6cb963522 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java @@ -128,7 +128,7 @@ public class MtpDatabaseTest extends AndroidTestCase { cursor.moveToNext(); assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID)); assertEquals( - Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, + Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY, getInt(cursor, Root.COLUMN_FLAGS)); assertEquals(R.drawable.ic_root_mtp, getInt(cursor, Root.COLUMN_ICON)); assertEquals("Device Storage", getString(cursor, Root.COLUMN_TITLE)); diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java index 53bccdc4f92a..3f7e8b6d7192 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java @@ -210,7 +210,11 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { assertEquals(2, cursor.getCount()); cursor.moveToNext(); assertEquals("1", cursor.getString(0)); - assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); + assertEquals( + Root.FLAG_SUPPORTS_IS_CHILD | + Root.FLAG_SUPPORTS_CREATE | + Root.FLAG_LOCAL_ONLY, + cursor.getInt(1)); assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device A Storage A", cursor.getString(3)); assertEquals("1", cursor.getString(4)); @@ -225,7 +229,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { cursor.moveToNext(); cursor.moveToNext(); assertEquals("2", cursor.getString(0)); - assertEquals(Root.FLAG_SUPPORTS_IS_CHILD, cursor.getInt(1)); + assertEquals( + Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_LOCAL_ONLY, cursor.getInt(1)); assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device B Storage B", cursor.getString(3)); assertEquals("2", cursor.getString(4)); @@ -271,7 +276,9 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { cursor.moveToNext(); assertEquals("1", cursor.getString(0)); - assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); + assertEquals( + Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY, + cursor.getInt(1)); assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device A", cursor.getString(3)); assertEquals("1", cursor.getString(4)); @@ -279,7 +286,9 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { cursor.moveToNext(); assertEquals("2", cursor.getString(0)); - assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); + assertEquals( + Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY, + cursor.getInt(1)); assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device B Storage B", cursor.getString(3)); assertEquals("2", cursor.getString(4)); diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml index 5e9d9c8ada2a..79f2f8ba6465 100644 --- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml +++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml @@ -60,7 +60,7 @@ <item quantity="one">找到 <xliff:g id="COUNT_0">%1$s</xliff:g> 台打印机</item> </plurals> <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string> - <string name="printer_info_desc" msgid="7181988788991581654">"关于此打印机的更多信息"</string> + <string name="printer_info_desc" msgid="7181988788991581654">"此打印机的详细信息"</string> <string name="could_not_create_file" msgid="3425025039427448443">"无法创建文件"</string> <string name="print_services_disabled_toast" msgid="9089060734685174685">"部分打印服务已停用"</string> <string name="print_searching_for_printers" msgid="6550424555079932867">"正在搜索打印机"</string> diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index c16e08a7c428..11235efbaf0f 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Grootste"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Gepasmaak (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Hulp en terugvoer"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Kieslys"</string> </resources> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 6e9dcd7fc193..dbfd5d85ec38 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"በጣም ተለቅ ያለ"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ብጁ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"እገዛ እና ግብረመልስ"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"ምናሌ"</string> </resources> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index ee579dc30b00..092cf63c6791 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"أكبر مستوى"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"مخصص (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"المساعدة والتعليقات"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"القائمة"</string> </resources> diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml index 60d0169be4b0..2e58ae9b4e4e 100644 --- a/packages/SettingsLib/res/values-az-rAZ/strings.xml +++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Ən böyük"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Fərdi (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Yardım və rəy"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menyu"</string> </resources> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 060a5fb15be3..1f0ed53be45c 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Najveći"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Prilagođeni (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Pomoć i povratne informacije"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Meni"</string> </resources> diff --git a/packages/SettingsLib/res/values-be-rBY/strings.xml b/packages/SettingsLib/res/values-be-rBY/strings.xml index 03de8bab32c1..b8de297a9437 100644 --- a/packages/SettingsLib/res/values-be-rBY/strings.xml +++ b/packages/SettingsLib/res/values-be-rBY/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Найвялікшы"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Карыстальніцкі (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Даведка і водгукі"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string> </resources> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index f6596dc48add..683a4d9d6b98 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Най-голямо"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Персонализирано (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Помощ и отзиви"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string> </resources> diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml index 4a11198be5f9..9ba656f78c33 100644 --- a/packages/SettingsLib/res/values-bn-rBD/strings.xml +++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"বৃহত্তম"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"কাস্টম (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"সহায়তা ও মতামত"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"মেনু"</string> </resources> diff --git a/packages/SettingsLib/res/values-bs-rBA/strings.xml b/packages/SettingsLib/res/values-bs-rBA/strings.xml index 40a3630a4e55..f2c73232a59f 100644 --- a/packages/SettingsLib/res/values-bs-rBA/strings.xml +++ b/packages/SettingsLib/res/values-bs-rBA/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Najveće"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Prilagodi (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Pomoć i povratne informacije"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Meni"</string> </resources> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 99850a5d5cd4..332daf3e5e96 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Màxim"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalitzat (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Ajuda i suggeriments"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menú"</string> </resources> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 361ba1f0cef2..d0dbe748e0bf 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Největší"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Vlastní (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Nápověda a zpětná vazba"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Nabídka"</string> </resources> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 98f41fc8945d..3c4b95508277 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Størst"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Tilpasset (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Hjælp og feedback"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index c9da6c96c0ff..0fca8b9ad4b2 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Am größten"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Benutzerdefiniert (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Hilfe & Feedback"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menü"</string> </resources> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 05e4e64c383f..da1b03c8dc5c 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Μεγαλύτερα"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Προσαρμοσμένη (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Βοήθεια και σχόλια"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Μενού"</string> </resources> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 799802b86b3a..70fa5017488e 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Largest"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Help & feedback"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 799802b86b3a..70fa5017488e 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Largest"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Help & feedback"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 799802b86b3a..70fa5017488e 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Largest"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Help & feedback"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 08e739a00f50..63a5d873dc03 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Máximo"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Ayuda y comentarios"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menú"</string> </resources> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 3d2f6c132da0..6b67b1194e04 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Lo más grande posible"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Ayuda y sugerencias"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menú"</string> </resources> diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml index a7b0f64f9a4d..de9255ae46d4 100644 --- a/packages/SettingsLib/res/values-et-rEE/strings.xml +++ b/packages/SettingsLib/res/values-et-rEE/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Suurim"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Kohandatud (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Abi ja tagasiside"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menüü"</string> </resources> diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml index f476511b8106..bd8c11ddde81 100644 --- a/packages/SettingsLib/res/values-eu-rES/strings.xml +++ b/packages/SettingsLib/res/values-eu-rES/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Handiena"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Pertsonalizatua (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Laguntza eta iritziak"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menua"</string> </resources> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 44918513bcd7..027987fe8453 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"بزرگترین"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"سفارشی (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"راهنما و بازخورد"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"منو"</string> </resources> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index cdef968d90aa..a988ccf109dc 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Suurin"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Muokattu (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Ohje ja palaute"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Valikko"</string> </resources> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 958b8fd4d5d3..e22e93824f10 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"La plus grande"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personnalisée (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Aide et commentaires"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index cf08f3b5f237..e4a34d6d4f23 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Le plus grand"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personnalisé (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Aide et commentaires"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml index cdc4581ca80c..10a395766604 100644 --- a/packages/SettingsLib/res/values-gl-rES/strings.xml +++ b/packages/SettingsLib/res/values-gl-rES/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"O máis grande"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Axuda e suxestións"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menú"</string> </resources> diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml index 1b40e01737d1..0f3079d36a0e 100644 --- a/packages/SettingsLib/res/values-gu-rIN/strings.xml +++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"સૌથી મોટું"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"કસ્ટમ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"સહાય અને પ્રતિસાદ"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"મેનુ"</string> </resources> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 8dfbb4a58780..5407c215be95 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"सबसे बड़ा"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"कस्टम (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"सहायता और फ़ीडबैक"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"मेनू"</string> </resources> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 07e492581d41..e52f44160d47 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Najveće"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Prilagođeno (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Pomoć i povratne informacije"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Izbornik"</string> </resources> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 0d8ec26eb668..ac4a43c46f47 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Legnagyobb"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Egyéni (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Súgó és visszajelzés"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menü"</string> </resources> diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml index cb12fbfaf502..a31489c4e64f 100644 --- a/packages/SettingsLib/res/values-hy-rAM/strings.xml +++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Ամենամեծ"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Հատուկ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Օգնություն և հետադարձ կապ"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Ընտրացանկ"</string> </resources> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index ffdb60745571..5711ea704a41 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Terbesar"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"(<xliff:g id="DENSITYDPI">%d</xliff:g>) khusus"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Bantuan & masukan"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml index 25e13a4d69d8..44226ee35e15 100644 --- a/packages/SettingsLib/res/values-is-rIS/strings.xml +++ b/packages/SettingsLib/res/values-is-rIS/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Stærst"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Sérsniðið (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Hjálp og ábendingar"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Valmynd"</string> </resources> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 2907fabef70e..b90fe662f69f 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -328,7 +328,7 @@ <string name="disabled_by_admin" msgid="3669999613095206948">"Disattivata dall\'amministratore"</string> <string name="home" msgid="3256884684164448244">"Home page Impostazioni"</string> <string-array name="battery_labels"> - <item msgid="8494684293649631252">"0%%"</item> + <item msgid="8494684293649631252">"0%"</item> <item msgid="8934126114226089439">"50%%"</item> <item msgid="1286113608943010849">"100%%"</item> </string-array> @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Massimo"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizzato (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Guida e feedback"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 7c16e4276347..6f7460a6639d 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"הכי גדול"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"מותאם אישית (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"עזרה ומשוב"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"תפריט"</string> </resources> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 25f4152a22fa..025b7fde4ed1 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -343,4 +343,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"最大"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"カスタム(<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"ヘルプとフィードバック"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"メニュー"</string> </resources> diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml index ffe194a0bc29..0eda6a94d1fc 100644 --- a/packages/SettingsLib/res/values-ka-rGE/strings.xml +++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"უდიდესი"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"მორგებული (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"დახმარება და გამოხმაურება"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"მენიუ"</string> </resources> diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml index 3dcc7eb50ced..369a10f3b3b0 100644 --- a/packages/SettingsLib/res/values-kk-rKZ/strings.xml +++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Ең үлкен"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Арнаулы (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Анықтама және пікір"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Mәзір"</string> </resources> diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml index aa0ce2498737..3e934bab59a3 100644 --- a/packages/SettingsLib/res/values-km-rKH/strings.xml +++ b/packages/SettingsLib/res/values-km-rKH/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ធំបំផុត"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ផ្ទាល់ខ្លួន (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"ជំនួយ និងមតិស្ថាបនា"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"ម៉ឺនុយ"</string> </resources> diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml index 643875fa6ea0..5a8d53cfe11a 100644 --- a/packages/SettingsLib/res/values-kn-rIN/strings.xml +++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ದೊಡ್ಡ"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ಕಸ್ಟಮ್ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"ಸಹಾಯ ಮತ್ತು ಪ್ರತಿಕ್ರಿಯೆ"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"ಮೆನು"</string> </resources> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index aaf34631b7fb..bd9633491ade 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -340,5 +340,6 @@ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"더 크게"</string> <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"가장 크게"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"맞춤(<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> - <string name="help_feedback_label" msgid="6815040660801785649">"도움말 및 의견"</string> + <string name="help_feedback_label" msgid="6815040660801785649">"고객센터"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"메뉴"</string> </resources> diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml index 96c1ec024aa6..5ca4e7c3fb21 100644 --- a/packages/SettingsLib/res/values-ky-rKG/strings.xml +++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Эң чоң"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Ыңгайлаштырылган (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Жардам жана жооп пикир"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string> </resources> diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml index 24f0c16c4962..b47629af0445 100644 --- a/packages/SettingsLib/res/values-lo-rLA/strings.xml +++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ໃຫຍ່ທີ່ສຸດ"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ປັບແຕ່ງເອງ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"ຊ່ວຍເຫຼືອ & ຄຳຕິຊົມ"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"ເມນູ"</string> </resources> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 4b8890d04d33..7ac54a8e925c 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Didžiausias"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Tinkintas (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Pagalba ir atsiliepimai"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Meniu"</string> </resources> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 4d76adc3e291..933d37447775 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Vislielākais"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Pielāgots (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Palīdzība un atsauksmes"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Izvēlne"</string> </resources> diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml index 953360a7aa00..1e1c857e1212 100644 --- a/packages/SettingsLib/res/values-mk-rMK/strings.xml +++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Најголем"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Приспособен (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Помош и повратни информации"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Мени"</string> </resources> diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml index 92c04487ace2..6f758860fbe3 100644 --- a/packages/SettingsLib/res/values-ml-rIN/strings.xml +++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ഏറ്റവും വലുത്"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ഇഷ്ടാനുസൃതം ( <xliff:g id="DENSITYDPI">%d</xliff:g> )"</string> <string name="help_feedback_label" msgid="6815040660801785649">"സഹായവും പ്രതികരണവും"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"മെനു"</string> </resources> diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml index 4c98c04c07d4..bf7d7cebc775 100644 --- a/packages/SettingsLib/res/values-mn-rMN/strings.xml +++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Хамгийн том"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Тогтмол утга (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Тусламж, санал хүсэлт"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Цэс"</string> </resources> diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml index e3a7cc46cd44..a1123bcbdb35 100644 --- a/packages/SettingsLib/res/values-mr-rIN/strings.xml +++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"सर्वात मोठा"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"सानुकूल करा (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"मदत आणि अभिप्राय"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"मेनू"</string> </resources> diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml index a1caa2a94419..8f115890ca7e 100644 --- a/packages/SettingsLib/res/values-ms-rMY/strings.xml +++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Terbesar"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Tersuai (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Bantuan & maklum balas"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml index 6a1c33190486..cfba5dadb43f 100644 --- a/packages/SettingsLib/res/values-my-rMM/strings.xml +++ b/packages/SettingsLib/res/values-my-rMM/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"အကြီးဆုံး"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"စိတ်ကြိုက် (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"အကူအညီနှင့် အကြံပြုချက်"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"မီနူး"</string> </resources> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 5d8ae55fb592..2da378aee46a 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Størst"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Egendefinert (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Hjelp og tilbakemelding"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Meny"</string> </resources> diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml index 15cc8ea87c72..a56b65573357 100644 --- a/packages/SettingsLib/res/values-ne-rNP/strings.xml +++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"सबैभन्दा ठूलो"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"अनुकूलन (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"मद्दत र प्रतिक्रिया"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"मेनु"</string> </resources> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 12bdb4f30c06..11d92e516113 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Grootst"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Aangepast (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Help en feedback"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml index 21d11b00cd4b..d526064b0132 100644 --- a/packages/SettingsLib/res/values-pa-rIN/strings.xml +++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ਸਭ ਤੋਂ ਵੱਡਾ"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"ਮਦਦ ਅਤੇ ਪ੍ਰਤੀਕਰਮ"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"ਮੀਨੂ"</string> </resources> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index d5116a2e3d7c..dcbb934cea6c 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Największy"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Niestandardowe (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Pomoc i opinie"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index f0cfa23df334..fc2222542324 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Maior"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizada (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Ajuda e feedback"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 40dfc74806b3..c297de5d0121 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"O maior"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Ajuda e comentários"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index f0cfa23df334..fc2222542324 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Maior"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizada (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Ajuda e feedback"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 6cc0f87e1b31..70912f9eb9d7 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Cel mai mare"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizat (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Ajutor și feedback"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Meniu"</string> </resources> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index ad4db89418b3..b8de0c312bba 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Максимальный"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Другой (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Справка/отзыв"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string> </resources> diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml index 5efb4001a638..d9fed962bac6 100644 --- a/packages/SettingsLib/res/values-si-rLK/strings.xml +++ b/packages/SettingsLib/res/values-si-rLK/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"විශාලතම"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"අභිරුචි (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"උදව් සහ ප්රතිපෝෂණ"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"මෙනුව"</string> </resources> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 7a7e3d49d12d..951d9fb995cf 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Najväčšie"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Vlastné (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Pomocník a spätná väzba"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Ponuka"</string> </resources> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 10bff6e038a9..2428076f637c 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Največje"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Po meri (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Pomoč in povratne informacije"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Meni"</string> </resources> diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml index e4f0eaa29596..1645f10732fa 100644 --- a/packages/SettingsLib/res/values-sq-rAL/strings.xml +++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Më i madhi"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"I personalizuar (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Ndihma dhe komentet"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menyja"</string> </resources> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index fba58c50211c..04dfb24c0c54 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Највећи"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Прилагођени (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Помоћ и повратне информације"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Мени"</string> </resources> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index ecc728a81eec..4b42db50a999 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Störst"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Anpassad (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Hjälp och feedback"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Meny"</string> </resources> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 435a1bdd99be..f70a1fa557db 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Kubwa zaidi"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Kiwango maalum (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Usaidizi na maoni"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menyu"</string> </resources> diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml index 033955c77e91..5f6b1215226b 100644 --- a/packages/SettingsLib/res/values-ta-rIN/strings.xml +++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"மிகப் பெரியது"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"தனிப்பயன் (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"உதவி & கருத்து"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"மெனு"</string> </resources> diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml index 57581765ae4e..ff8b8550cda7 100644 --- a/packages/SettingsLib/res/values-te-rIN/strings.xml +++ b/packages/SettingsLib/res/values-te-rIN/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"అతి పెద్దగా"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"అనుకూలం (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"సహాయం & అభిప్రాయం"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"మెను"</string> </resources> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 4849e1955321..4d0a2e4ead46 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ใหญ่ที่สุด"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"กำหนดเอง (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"ความช่วยเหลือและความคิดเห็น"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"เมนู"</string> </resources> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 24f5499d4490..95897a325136 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Pinakamalaki"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Tulong at feedback"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 950e3221ff7c..3c5b46d46bbe 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"En büyük"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Özel (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Yardım ve geri bildirim"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menü"</string> </resources> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 6b49b14084e9..ef30a69a3eb4 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Найбільші елементи"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Спеціальний масштаб (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Довідка й відгуки"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string> </resources> diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml index 2ac8a6145797..57b0b63e37e9 100644 --- a/packages/SettingsLib/res/values-ur-rPK/strings.xml +++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"سب سے بڑا"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"حسب ضرورت (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"مدد اور تاثرات"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"مینو"</string> </resources> diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml index 9022ad31dacf..b4e05052ce16 100644 --- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml +++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Eng katta"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Moslashtirilgan (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Yordam va fikr-mulohaza"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menyu"</string> </resources> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index c58f84993bb6..494cb7cfd23c 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Lớn nhất"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Tùy chỉnh (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Trợ giúp và phản hồi"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string> </resources> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 18ae80e3d9b6..1c58a1145c08 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"最大"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"自定义 (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"帮助和反馈"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"菜单"</string> </resources> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 660071df7073..45918f0a5583 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"最大"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"自訂 (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"說明與意見反映"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"選單"</string> </resources> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index bce6f244d1e4..b2e0d12bf98d 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"最大"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"自訂 (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"說明與意見回饋"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"選單"</string> </resources> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 07c6fce2c3f9..d1379086c302 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -341,4 +341,5 @@ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Okukhulu kakhulu"</string> <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Ngokwezifiso (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="help_feedback_label" msgid="6815040660801785649">"Usizo nempendulo"</string> + <string name="content_description_menu_button" msgid="8182594799812351266">"Imenyu"</string> </resources> diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml index 299a5b74689c..d9473fa0b8b3 100755 --- a/packages/SettingsLib/res/values/config.xml +++ b/packages/SettingsLib/res/values/config.xml @@ -19,4 +19,7 @@ <resources> <!-- Configuration for automotive --> <bool name="enable_pbap_pce_profile">false</bool> + + <!-- Default data warning level in mb --> + <integer name="default_data_warning_level_mb">2048</integer> </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java index 687b3fc68542..2e77f4264de6 100644 --- a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java +++ b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java @@ -178,8 +178,9 @@ public class NetworkPolicyEditor { public void setPolicyWarningBytes(NetworkTemplate template, long warningBytes) { long limitBytes = getPolicyLimitBytes(template); - // If the warningBytes are larger than limitBytes, set the warningBytes to limitBytes - warningBytes = Math.min(warningBytes, limitBytes); + warningBytes = + (limitBytes == LIMIT_DISABLED) ? warningBytes : Math.min(warningBytes, limitBytes); + setPolicyWarningBytesInner(template, warningBytes); } @@ -192,8 +193,7 @@ public class NetworkPolicyEditor { public void setPolicyLimitBytes(NetworkTemplate template, long limitBytes) { long warningBytes = getPolicyWarningBytes(template); - // If the warningBytes are larger than limitBytes, set the warningBytes to limitBytes - if (warningBytes > limitBytes) { + if (warningBytes > limitBytes && limitBytes != LIMIT_DISABLED) { setPolicyWarningBytesInner(template, limitBytes); } diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java index e53dd2f42c75..994ea88a80dd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java @@ -32,6 +32,8 @@ import android.text.format.DateUtils; import android.text.format.Time; import android.util.Log; +import com.android.settingslib.R; + import java.util.Date; import java.util.Locale; @@ -41,12 +43,12 @@ import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; import static android.telephony.TelephonyManager.SIM_STATE_READY; import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; import static android.text.format.DateUtils.FORMAT_SHOW_DATE; +import static android.net.TrafficStats.MB_IN_BYTES; public class DataUsageController { + private static final String TAG = "DataUsageController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - public static final long DEFAULT_WARNING_LEVEL = 2L * 1024 * 1024 * 1024; private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES; private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50); private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter( @@ -75,6 +77,14 @@ public class DataUsageController { mNetworkController = networkController; } + /** + * Returns the default warning level in bytes. + */ + public long getDefaultWarningLevel() { + return MB_IN_BYTES + * mContext.getResources().getInteger(R.integer.default_data_warning_level_mb); + } + private INetworkStatsSession getSession() { if (mSession == null) { try { @@ -169,7 +179,7 @@ public class DataUsageController { usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0; usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0; } else { - usage.warningLevel = DEFAULT_WARNING_LEVEL; + usage.warningLevel = getDefaultWarningLevel(); } if (usage != null && mNetworkController != null) { usage.carrier = mNetworkController.getMobileDataNetworkName(); diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java index 5c8849da3876..c189e1d51499 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java @@ -31,6 +31,7 @@ import android.graphics.drawable.Drawable; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.Log; import android.view.inputmethod.InputMethodInfo; @@ -49,6 +50,7 @@ public class AppRestrictionsHelper { private static final boolean DEBUG = false; private static final String TAG = "AppRestrictionsHelper"; + private final Injector mInjector; private final Context mContext; private final PackageManager mPackageManager; private final IPackageManager mIPm; @@ -61,11 +63,17 @@ public class AppRestrictionsHelper { private List<SelectableAppInfo> mVisibleApps; public AppRestrictionsHelper(Context context, UserHandle user) { - mContext = context; - mPackageManager = context.getPackageManager(); - mIPm = AppGlobals.getPackageManager(); - mUser = user; - mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + this(new Injector(context, user)); + } + + @VisibleForTesting + AppRestrictionsHelper(Injector injector) { + mInjector = injector; + mContext = mInjector.getContext(); + mPackageManager = mInjector.getPackageManager(); + mIPm = mInjector.getIPackageManager(); + mUser = mInjector.getUser(); + mUserManager = mInjector.getUserManager(); mRestrictedProfile = mUserManager.getUserInfo(mUser.getIdentifier()).isRestricted(); } @@ -86,8 +94,7 @@ public class AppRestrictionsHelper { } public void applyUserAppsStates(OnDisableUiForPackageListener listener) { - final int userId = mUser.getIdentifier(); - if (!mUserManager.getUserInfo(userId).isRestricted() && userId != UserHandle.myUserId()) { + if (!mRestrictedProfile && mUser.getIdentifier() != UserHandle.myUserId()) { Log.e(TAG, "Cannot apply application restrictions on another user!"); return; } @@ -130,8 +137,8 @@ public class AppRestrictionsHelper { ApplicationInfo info = mIPm.getApplicationInfo(packageName, 0, userId); if (info != null) { if (mRestrictedProfile) { - mIPm.deletePackageAsUser(packageName, null, mUser.getIdentifier(), - PackageManager.DELETE_SYSTEM_APP); + mPackageManager.deletePackageAsUser(packageName, null, + PackageManager.DELETE_SYSTEM_APP, mUser.getIdentifier()); if (DEBUG) { Log.d(TAG, "Uninstalling " + packageName); } @@ -268,9 +275,7 @@ public class AppRestrictionsHelper { * @param excludePackages the set of package names to append to */ private void addSystemImes(Set<String> excludePackages) { - InputMethodManager imm = (InputMethodManager) - mContext.getSystemService(Context.INPUT_METHOD_SERVICE); - List<InputMethodInfo> imis = imm.getInputMethodList(); + List<InputMethodInfo> imis = mInjector.getInputMethodList(); for (InputMethodInfo imi : imis) { try { if (imi.isDefault(mContext) && isSystemPackage(imi.getPackageName())) { @@ -376,4 +381,44 @@ public class AppRestrictionsHelper { return lhsLabel.toLowerCase().compareTo(rhsLabel.toLowerCase()); } } + + /** + * Unit test will subclass it to inject mocks. + */ + @VisibleForTesting + static class Injector { + private Context mContext; + private UserHandle mUser; + + Injector(Context context, UserHandle user) { + mContext = context; + mUser = user; + } + + Context getContext() { + return mContext; + } + + UserHandle getUser() { + return mUser; + } + + PackageManager getPackageManager() { + return mContext.getPackageManager(); + } + + IPackageManager getIPackageManager() { + return AppGlobals.getPackageManager(); + } + + UserManager getUserManager() { + return mContext.getSystemService(UserManager.class); + } + + List<InputMethodInfo> getInputMethodList() { + InputMethodManager imm = (InputMethodManager) getContext().getSystemService( + Context.INPUT_METHOD_SERVICE); + return imm.getInputMethodList(); + } + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java index 284827b57929..aae9cf6de797 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java @@ -29,7 +29,6 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.SparseArray; import android.widget.TextView; - import com.android.settingslib.R; public class AccessPointPreference extends Preference { @@ -44,13 +43,14 @@ public class AccessPointPreference extends Preference { private final StateListDrawable mWifiSld; private final int mBadgePadding; private final UserBadgeCache mBadgeCache; - private TextView mTitleView; + private boolean mForSavedNetworks = false; private AccessPoint mAccessPoint; private Drawable mBadge; private int mLevel; private CharSequence mContentDescription; + private int mDefaultIconResId; static final int[] WIFI_CONNECTION_STRENGTH = { R.string.accessibility_wifi_one_bar, @@ -85,6 +85,24 @@ public class AccessPointPreference extends Preference { refresh(); } + public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache, + int iconResId, boolean forSavedNetworks) { + super(context); + mBadgeCache = cache; + mAccessPoint = accessPoint; + mForSavedNetworks = forSavedNetworks; + mAccessPoint.setTag(this); + mLevel = -1; + mDefaultIconResId = iconResId; + + mWifiSld = (StateListDrawable) context.getTheme() + .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0); + + // Distance from the end of the title at which this AP's user badge should sit. + mBadgePadding = context.getResources() + .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding); + } + public AccessPoint getAccessPoint() { return mAccessPoint; } @@ -112,7 +130,7 @@ public class AccessPointPreference extends Preference { protected void updateIcon(int level, Context context) { if (level == -1) { - setIcon(null); + safeSetDefaultIcon(); } else { if (getIcon() == null) { // To avoid a drawing race condition, we first set the state (SECURE/NONE) and then @@ -124,16 +142,24 @@ public class AccessPointPreference extends Preference { ? STATE_SECURED : STATE_NONE); Drawable drawable = mWifiSld.getCurrent(); - if (!mForSavedNetworks) { + if (!mForSavedNetworks && drawable != null) { setIcon(drawable); - } else { - setIcon(null); + return; } } + safeSetDefaultIcon(); } } } + private void safeSetDefaultIcon() { + if (mDefaultIconResId != 0) { + setIcon(mDefaultIconResId); + } else { + setIcon(null); + } + } + protected void updateBadge(Context context) { WifiConfiguration config = mAccessPoint.getConfig(); if (config != null) { diff --git a/packages/SettingsLib/tests/src/com/android/settingslib/users/AppRestrictionsHelperTest.java b/packages/SettingsLib/tests/src/com/android/settingslib/users/AppRestrictionsHelperTest.java new file mode 100644 index 000000000000..3f989bd20065 --- /dev/null +++ b/packages/SettingsLib/tests/src/com/android/settingslib/users/AppRestrictionsHelperTest.java @@ -0,0 +1,305 @@ +/* + * 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.settingslib.users; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; + +import android.appwidget.AppWidgetManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageDeleteObserver; +import android.content.pm.IPackageManager; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.UserInfo; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; +import android.test.suitebuilder.annotation.SmallTest; +import android.view.inputmethod.InputMethodInfo; +import com.android.settingslib.BaseTest; + +import org.hamcrest.Description; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; + +@SmallTest +public class AppRestrictionsHelperTest extends BaseTest { + private @Mock Context mContext; + private @Mock PackageManager mPm; + private @Mock IPackageManager mIpm; + private @Mock UserManager mUm; + + private TestInjector mInjector; + private UserHandle mTestUser = UserHandle.of(1111); + private AppRestrictionsHelper mHelper; + + private ArrayList<String> mInstalledApps; + private ArrayList<ApplicationInfo> mInstalledAppInfos; + + @Override + protected void setUp() throws Exception { + super.setUp(); + MockitoAnnotations.initMocks(this); + + mInjector = new TestInjector(); + final UserInfo user = new UserInfo( + mTestUser.getIdentifier(), "test_user", UserInfo.FLAG_RESTRICTED); + when(mUm.getUserInfo(mTestUser.getIdentifier())).thenReturn(user); + mHelper = new AppRestrictionsHelper(mInjector); + mInstalledApps = new ArrayList<>(); + mInstalledAppInfos = new ArrayList<>(); + } + + public void testFetchAndMergeApps() throws Exception { + addSystemAppsWithRequiredAccounts("sys.app0"); + addsystemImes(new String[] {"sys.app1", "sys.app2"}, + new String[] {"sys.app3", "sys.app4", "sys.app5"}); + addSystemAppsForIntent(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER), + "sys.app1", "sys.app4", "sys.app6"); + addSystemAppsForIntent(new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE), + "sys.app2", "sys.app5", "sys.app7"); + addDownloadedApps("app1", "app2"); + when(mPm.getInstalledApplications(anyInt())).thenReturn(mInstalledAppInfos); + + mHelper.fetchAndMergeApps(); + + final ArrayList<String> notExpectedInVisibleApps = new ArrayList<>(); + // System apps that require an account and doesn't see restricted account are + // not part of visibleApps. + notExpectedInVisibleApps.add("sys.app0"); + // Default system IMEs are not part of visibleApps. + notExpectedInVisibleApps.add("sys.app1"); + notExpectedInVisibleApps.add("sys.app2"); + + final ArrayList<String> expectedInVisibleApps = new ArrayList<>(); + expectedInVisibleApps.add("sys.app4"); + expectedInVisibleApps.add("sys.app5"); + expectedInVisibleApps.add("sys.app6"); + expectedInVisibleApps.add("sys.app7"); + expectedInVisibleApps.add("app1"); + expectedInVisibleApps.add("app2"); + + for (AppRestrictionsHelper.SelectableAppInfo info : mHelper.getVisibleApps()) { + if (expectedInVisibleApps.contains(info.packageName)) { + expectedInVisibleApps.remove(info.packageName); + } else if (notExpectedInVisibleApps.contains(info.packageName)) { + fail("Package: " + info.packageName + " should not be included in visibleApps"); + } else { + fail("Unknown package: " + info.packageName); + } + } + assertEquals("Some expected apps are not inclued in visibleApps: " + expectedInVisibleApps, + 0, expectedInVisibleApps.size()); + + assertFalse("System apps that require an account and doesn't see restricted account " + + "should be marked for removal", mHelper.isPackageSelected("sys.app0")); + } + + public void testApplyUserAppsStates() throws Exception { + final int testUserId = mTestUser.getIdentifier(); + mHelper.setPackageSelected("app1", true); + + mHelper.setPackageSelected("app2", true); + ApplicationInfo info = new ApplicationInfo(); + info.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN; + info.flags |= ApplicationInfo.FLAG_INSTALLED; + when(mIpm.getApplicationInfo(eq("app2"), anyInt(), eq(testUserId))) + .thenReturn(info); + + mHelper.setPackageSelected("app3", false); + info = new ApplicationInfo(); + when(mIpm.getApplicationInfo(eq("app3"), anyInt(), eq(testUserId))) + .thenReturn(info); + + AppRestrictionsHelper.OnDisableUiForPackageListener mockListener = + mock(AppRestrictionsHelper.OnDisableUiForPackageListener.class); + mHelper.applyUserAppsStates(mockListener); + + verify(mIpm, times(1)).installExistingPackageAsUser("app1", testUserId); + verify(mIpm, times(1)).setApplicationHiddenSettingAsUser("app2", false, testUserId); + verify(mockListener).onDisableUiForPackage("app2"); + verify(mPm, times(1)).deletePackageAsUser(eq("app3"), any(IPackageDeleteObserver.class), + anyInt(), eq(mTestUser.getIdentifier())); + } + + private void addsystemImes(String[] defaultImes, String[] otherImes) throws + PackageManager.NameNotFoundException, RemoteException { + final ArrayList<InputMethodInfo> inputMethods = new ArrayList<>(); + for (String pkg : defaultImes) { + final ResolveInfo ri = createResolveInfoForSystemApp(pkg); + final InputMethodInfo inputMethodInfo = new InputMethodInfo( + ri, false, null, null, 0, true, true); + inputMethods.add(inputMethodInfo); + addInstalledApp(ri); + } + for (String pkg : otherImes) { + final ResolveInfo ri = createResolveInfoForSystemApp(pkg); + final InputMethodInfo inputMethodInfo = new InputMethodInfo( + ri, false, null, null, 0, false, true); + inputMethods.add(inputMethodInfo); + addInstalledApp(ri); + } + + mInjector.setInputMethodList(inputMethods); + } + + private void addSystemAppsForIntent(Intent intent, String... packages) throws Exception { + List<ResolveInfo> resolveInfos = new ArrayList<>(); + for (String pkg : packages) { + final ResolveInfo ri = createResolveInfoForSystemApp(pkg); + resolveInfos.add(ri); + addInstalledApp(ri); + } + when(mPm.queryIntentActivities(argThat(new IntentMatcher(intent)), anyInt())) + .thenReturn(resolveInfos); + } + + private void addSystemAppsWithRequiredAccounts(String... packages) throws Exception { + for (String pkg : packages) { + final ResolveInfo ri = createResolveInfoForSystemApp(pkg); + final PackageInfo packageInfo = new PackageInfo(); + packageInfo.applicationInfo = ri.activityInfo.applicationInfo; + packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED; + packageInfo.requiredAccountType = "account"; + packageInfo.restrictedAccountType = null; + mInstalledAppInfos.add(packageInfo.applicationInfo); + when(mPm.getPackageInfo(eq(pkg), anyInt())).thenReturn(packageInfo); + } + } + + private void addDownloadedApps(String... packages) throws Exception { + for (String pkg : packages) { + final ResolveInfo ri = createResolveInfo(pkg); + addInstalledApp(ri); + } + } + + private void addInstalledApp(ResolveInfo ri) throws PackageManager.NameNotFoundException { + final String pkgName = ri.activityInfo.packageName; + if (mInstalledApps.contains(pkgName)) { + return; + } + final PackageInfo packageInfo = new PackageInfo(); + packageInfo.applicationInfo = ri.activityInfo.applicationInfo; + packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED; + mInstalledAppInfos.add(packageInfo.applicationInfo); + when(mPm.getPackageInfo(eq(pkgName), anyInt())).thenReturn(packageInfo); + } + + private ResolveInfo createResolveInfoForSystemApp(String packageName) { + final ResolveInfo ri = createResolveInfo(packageName); + ri.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; + ri.serviceInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; + return ri; + } + + private ResolveInfo createResolveInfo(String packageName) { + final ResolveInfo ri = new ResolveInfo(); + final ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.packageName = packageName; + final ActivityInfo activityInfo = new ActivityInfo(); + activityInfo.applicationInfo = applicationInfo; + activityInfo.packageName = packageName; + activityInfo.name = ""; + ri.activityInfo = activityInfo; + final ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.applicationInfo = applicationInfo; + serviceInfo.packageName = packageName; + serviceInfo.name = ""; + ri.serviceInfo = serviceInfo; + return ri; + } + + private class IntentMatcher extends ArgumentMatcher<Intent> { + private final Intent mIntent; + + IntentMatcher(Intent intent) { + mIntent = intent; + } + + @Override + public boolean matches(Object argument) { + if (argument instanceof Intent) { + return ((Intent) argument).filterEquals(mIntent); + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("Expected: " + mIntent); + } + } + + private class TestInjector extends AppRestrictionsHelper.Injector { + List<InputMethodInfo> mImis; + + TestInjector() { + super(mContext, mTestUser); + } + + @Override + Context getContext() { + return mContext; + } + + @Override + UserHandle getUser() { + return mTestUser; + } + + @Override + PackageManager getPackageManager() { + return mPm; + } + + @Override + IPackageManager getIPackageManager() { + return mIpm; + } + + @Override + UserManager getUserManager() { + return mUm; + } + + @Override + List<InputMethodInfo> getInputMethodList() { + return mImis; + } + + void setInputMethodList(List<InputMethodInfo> imis) { + mImis = imis; + } + } +} diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_carmode.png Binary files differindex 6242084ea175..95e5778448b2 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_carmode.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_carmode.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_carmode.png Binary files differindex 1b37a47338aa..6421146caae3 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_carmode.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_carmode.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_carmode.png Binary files differindex 9e0575883a06..151d5feee7eb 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_carmode.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_carmode.png diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_carmode.png Binary files differindex 2fcfdde08164..b954aa7adc0c 100644 --- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_carmode.png +++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_carmode.png diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_carmode.png Binary files differindex 48708a5099a8..61d5db659850 100644 --- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_carmode.png +++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_carmode.png diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_carmode.png Binary files differindex 3d731840a93f..7b98c1fa7504 100644 --- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_carmode.png +++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_carmode.png diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_carmode.png Binary files differindex 786935d5d185..aad13204b048 100644 --- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_carmode.png +++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_carmode.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_carmode.png Binary files differindex e4bd4bc3e39d..754b2d99ec29 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_carmode.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_carmode.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_carmode.png Binary files differindex 94ccf7912e8f..873ed7f9e744 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_carmode.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_carmode.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_carmode.png Binary files differindex 980bbbcb4ceb..7696d872fe21 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_carmode.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_carmode.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_carmode.png Binary files differindex 201be3b3c8b5..c98f55e071d8 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_carmode.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_carmode.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_carmode.png Binary files differindex 2770d6264da0..187a5661a38b 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_carmode.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_carmode.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_carmode.png Binary files differindex 8ac64937007b..c66f8be323d6 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_carmode.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_carmode.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_carmode.png Binary files differindex 8e3678b292b8..3a3a119041bd 100644 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_carmode.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_carmode.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_carmode.png Binary files differindex 6c7cb0582733..7198c825322f 100644 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_carmode.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_carmode.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_carmode.png Binary files differindex ea2b108e1e4c..b1fc02e51502 100644 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_carmode.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_carmode.png diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_carmode.png Binary files differindex 3e11023ef7e1..c06bfdab0abd 100644 --- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_carmode.png +++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_carmode.png diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_carmode.png Binary files differindex fe8213d6390e..a8c76bfc664c 100644 --- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_carmode.png +++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_carmode.png diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_carmode.png Binary files differindex c117efd1e654..b798e3d6ea06 100644 --- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_carmode.png +++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_carmode.png diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml index 0c8cc9bb5f02..f0c4e595106a 100644 --- a/packages/SystemUI/res/layout/remote_input.xml +++ b/packages/SystemUI/res/layout/remote_input.xml @@ -42,7 +42,6 @@ android:singleLine="true" android:ellipsize="start" android:inputType="textShortMessage|textAutoCorrect|textCapSentences" - android:textIsSelectable="true" android:imeOptions="actionNone|flagNoExtractUi" /> <FrameLayout diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 72420b2b88fb..808a2a158e7a 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -344,7 +344,7 @@ <string name="description_target_search" msgid="3091587249776033139">"ค้นหา"</string> <string name="description_direction_up" msgid="7169032478259485180">"เลื่อนขึ้นเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string> <string name="description_direction_left" msgid="7207478719805562165">"เลื่อนไปทางซ้ายเพื่อ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>"</string> - <string name="zen_priority_introduction" msgid="3070506961866919502">"คุณจะไม่ถูกรบกวนจากเสียงและการสั่น ยกเว้นการปลุก การเตือนความจำ กิจกรรม และผู้โทรที่คุณระบุ"</string> + <string name="zen_priority_introduction" msgid="3070506961866919502">"คุณจะไม่ถูกรบกวนจากเสียงและการสั่น ยกเว้นการปลุก การช่วยเตือน กิจกรรม และผู้โทรที่คุณระบุ"</string> <string name="zen_priority_customize_button" msgid="7948043278226955063">"กำหนดค่า"</string> <string name="zen_silence_introduction_voice" msgid="2284540992298200729">"การใช้โหมดนี้จะบล็อกเสียงและการสั่นทั้งหมด ซึ่งรวมถึงเสียงปลุก เพลง วิดีโอ และเกม คุณจะยังโทรออกได้อยู่"</string> <string name="zen_silence_introduction" msgid="3137882381093271568">"การใช้โหมดนี้จะบล็อกเสียงและการสั่นทั้งหมด ซึ่งรวมถึงเสียงปลุก เพลง วิดีโอ และเกม"</string> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index c4ef80f004b5..2ab2a8cc15f3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -16,7 +16,11 @@ package com.android.systemui.keyguard; -import android.annotation.UserIdInt; +import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; + import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerNative; @@ -82,12 +86,6 @@ import com.android.systemui.statusbar.phone.StatusBarWindowManager; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.List; - -import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; -import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; -import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; -import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; /** * Mediates requests related to the keyguard. This includes queries about the @@ -512,7 +510,12 @@ public class KeyguardViewMediator extends SystemUI { } } - + @Override + public void onHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper) { + synchronized (KeyguardViewMediator.this) { + notifyHasLockscreenWallpaperChanged(hasLockscreenWallpaper); + } + } }; ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() { @@ -2035,6 +2038,21 @@ public class KeyguardViewMediator extends SystemUI { } } + private void notifyHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper) { + int size = mKeyguardStateCallbacks.size(); + for (int i = size - 1; i >= 0; i--) { + try { + mKeyguardStateCallbacks.get(i).onHasLockscreenWallpaperChanged( + hasLockscreenWallpaper); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call onHasLockscreenWallpaperChanged", e); + if (e instanceof DeadObjectException) { + mKeyguardStateCallbacks.remove(i); + } + } + } + } + public void addStateMonitorCallback(IKeyguardStateCallback callback) { synchronized (this) { mKeyguardStateCallbacks.add(callback); @@ -2044,6 +2062,7 @@ public class KeyguardViewMediator extends SystemUI { callback.onInputRestrictedStateChanged(mInputRestricted); callback.onTrustedChanged(mUpdateMonitor.getUserHasTrust( KeyguardUpdateMonitor.getCurrentUser())); + callback.onHasLockscreenWallpaperChanged(mUpdateMonitor.hasLockscreenWallpaper()); } catch (RemoteException e) { Slog.w(TAG, "Failed to call to IKeyguardStateCallback", e); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java index 65d680509be2..15ae4ad6dac0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java @@ -126,8 +126,8 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene ? R.drawable.ic_qs_branded_vpn : R.drawable.ic_qs_vpn); if (mFooterIconId != footerIconId) { - mFooterIcon.setImageResource(footerIconId); mFooterIconId = footerIconId; + mMainHandler.post(mUpdateIcon); } mIsVisible = mIsIconVisible; } @@ -207,6 +207,13 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene } } + private final Runnable mUpdateIcon = new Runnable() { + @Override + public void run() { + mFooterIcon.setImageResource(mFooterIconId); + } + }; + private final Runnable mUpdateDisplayState = new Runnable() { @Override public void run() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 2673ee8cb9f5..a6536a83183b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -2521,8 +2521,7 @@ public abstract class BaseStatusBar extends SystemUI implements boolean inUse = mPowerManager.isScreenOn() && (!mStatusBarKeyguardViewManager.isShowing() - || mStatusBarKeyguardViewManager.isOccluded()) - && !mStatusBarKeyguardViewManager.isInputRestricted(); + || mStatusBarKeyguardViewManager.isOccluded()); try { inUse = inUse && !mDreamManager.isDreaming(); } catch (RemoteException e) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 048c4bd4a770..8cabfb93bf32 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -461,7 +461,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL // happens to occur during the launch. ActivityOptions o = ActivityOptions.makeBasic(); o.setRotationAnimationHint( - WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE); + WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS); try { result = ActivityManagerNative.getDefault().startActivityAsUser( null, getContext().getBasePackageName(), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java index 1343c3ad0307..af6e2595f0b6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java @@ -38,6 +38,8 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.util.Log; +import com.android.keyguard.KeyguardUpdateMonitor; + import libcore.io.IoUtils; import java.util.Objects; @@ -52,6 +54,7 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen private final PhoneStatusBar mBar; private final WallpaperManager mWallpaperManager; private final Handler mH; + private final KeyguardUpdateMonitor mUpdateMonitor; private boolean mCached; private Bitmap mCache; @@ -66,6 +69,7 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen mH = h; mWallpaperManager = (WallpaperManager) ctx.getSystemService(Context.WALLPAPER_SERVICE); mCurrentUserId = ActivityManager.getCurrentUser(); + mUpdateMonitor = KeyguardUpdateMonitor.getInstance(ctx); IWallpaperManager service = IWallpaperManager.Stub.asInterface( ServiceManager.getService(Context.WALLPAPER_SERVICE)); @@ -89,6 +93,7 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen LoaderResult result = loadBitmap(mCurrentUserId, mSelectedUser); if (result.success) { mCached = true; + mUpdateMonitor.setHasLockscreenWallpaper(result.bitmap != null); mCache = result.bitmap; } return mCache; @@ -181,6 +186,7 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen if (result.success) { mCached = true; mCache = result.bitmap; + mUpdateMonitor.setHasLockscreenWallpaper(result.bitmap != null); mBar.updateMediaMetaData( true /* metaDataChanged */, true /* allowEnterAnimation */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index aae91b18bc80..222e8dfa2c82 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -54,7 +54,7 @@ import java.io.PrintWriter; public class NavigationBarView extends LinearLayout { final static boolean DEBUG = false; - final static String TAG = "PhoneStatusBar/NavigationBarView"; + final static String TAG = "StatusBar/NavBarView"; // slippery nav bar when everything is disabled, e.g. during setup final static boolean SLIPPERY_WHEN_DISABLED = true; @@ -527,8 +527,8 @@ public class NavigationBarView extends LinearLayout { updateCurrentView(); } - public boolean needsReorient() { - return mCurrentRotation != mDisplay.getRotation(); + public boolean needsReorient(int rotation) { + return mCurrentRotation != rotation; } private void updateCurrentView() { @@ -567,7 +567,7 @@ public class NavigationBarView extends LinearLayout { setMenuVisibility(mShowMenu, true /* force */); if (DEBUG) { - Log.d(TAG, "reorient(): rot=" + mDisplay.getRotation()); + Log.d(TAG, "reorient(): rot=" + mCurrentRotation); } updateTaskSwitchHelper(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java index 1755cc68bee2..2c8339a1dc22 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -42,7 +42,7 @@ public class NotificationGroupManager implements HeadsUpManager.OnHeadsUpChanged private int mBarState = -1; private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>(); private HeadsUpManager mHeadsUpManager; - private boolean mUpdatingSuppressionBlocked; + private boolean mIsUpdatingUnchangedGroup; public void setOnGroupChangeListener(OnGroupChangeListener listener) { mListener = listener; @@ -141,7 +141,7 @@ public class NotificationGroupManager implements HeadsUpManager.OnHeadsUpChanged } private void updateSuppression(NotificationGroup group) { - if (group == null || mUpdatingSuppressionBlocked) { + if (group == null) { return; } boolean prevSuppressed = group.suppressed; @@ -154,7 +154,9 @@ public class NotificationGroupManager implements HeadsUpManager.OnHeadsUpChanged if (group.suppressed) { handleSuppressedSummaryHeadsUpped(group.summary); } - mListener.onGroupsChanged(); + if (!mIsUpdatingUnchangedGroup) { + mListener.onGroupsChanged(); + } } } @@ -188,12 +190,12 @@ public class NotificationGroupManager implements HeadsUpManager.OnHeadsUpChanged boolean groupKeysChanged = !oldKey.equals(newKey); boolean wasGroupChild = isGroupChild(oldNotification); boolean isGroupChild = isGroupChild(entry.notification); - mUpdatingSuppressionBlocked = !groupKeysChanged && wasGroupChild == isGroupChild; + mIsUpdatingUnchangedGroup = !groupKeysChanged && wasGroupChild == isGroupChild; if (mGroupMap.get(getGroupKey(oldNotification)) != null) { onEntryRemovedInternal(entry, oldNotification); } onEntryAdded(entry); - mUpdatingSuppressionBlocked = false; + mIsUpdatingUnchangedGroup = false; if (isIsolated(entry.notification)) { mIsolatedEntries.put(entry.key, entry.notification); if (groupKeysChanged) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index e95cc6b3fed3..d2bb40565ec4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -65,7 +65,6 @@ import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.hardware.display.DisplayManager; import android.inputmethodservice.InputMethodService; import android.media.AudioAttributes; import android.media.MediaMetadata; @@ -100,6 +99,7 @@ import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.view.Display; +import android.view.IRotationWatcher; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -207,7 +207,7 @@ import java.util.Map; public class PhoneStatusBar extends BaseStatusBar implements DemoMode, DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener, - HeadsUpManager.OnHeadsUpChangedListener, DisplayManager.DisplayListener { + HeadsUpManager.OnHeadsUpChangedListener { static final String TAG = "PhoneStatusBar"; public static final boolean DEBUG = BaseStatusBar.DEBUG; public static final boolean SPEW = false; @@ -694,8 +694,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mUnlockMethodCache.addListener(this); startKeyguard(); - mContext.getSystemService(DisplayManager.class).registerDisplayListener(this, null); - mDozeServiceHost = new DozeServiceHost(); KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mDozeServiceHost); putComponent(DozeHost.class, mDozeServiceHost); @@ -1415,6 +1413,27 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView); if (mNavigationBarView == null) return; + try { + WindowManagerGlobal.getWindowManagerService() + .watchRotation(new IRotationWatcher.Stub() { + @Override + public void onRotationChanged(int rotation) throws RemoteException { + // We need this to be scheduled as early as possible to beat the redrawing of + // window in response to the orientation change. + Message msg = Message.obtain(mHandler, () -> { + if (mNavigationBarView != null + && mNavigationBarView.needsReorient(rotation)) { + repositionNavigationBar(); + } + }); + msg.setAsynchronous(true); + mHandler.sendMessageAtFrontOfQueue(msg); + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + prepareNavigationBarView(); mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams()); @@ -3431,7 +3450,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // force the crossfade animation if an orientation change // happens to occur during the launch. options.setRotationAnimationHint( - WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE); + WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS); } try { result = ActivityManagerNative.getDefault().startActivityAsUser( @@ -3599,22 +3618,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void onDisplayAdded(int displayId) { - } - - @Override - public void onDisplayRemoved(int displayId) { - } - - @Override - public void onDisplayChanged(int displayId) { - if (displayId == Display.DEFAULT_DISPLAY - && mNavigationBarView != null && mNavigationBarView.needsReorient()) { - repositionNavigationBar(); - } - } - - @Override public void userSwitched(int newUserId) { super.userSwitched(newUserId); if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 22c67c952e5d..4779da617323 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -413,7 +413,10 @@ public class NotificationStackScrollLayout extends ViewGroup @Override protected void onDraw(Canvas canvas) { - canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom, mBackgroundPaint); + if (mCurrentBounds.top < mCurrentBounds.bottom) { + canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom, + mBackgroundPaint); + } if (DEBUG) { int y = mTopPadding; canvas.drawLine(0, y, getWidth(), y, mDebugPaint); @@ -2049,11 +2052,12 @@ public class NotificationStackScrollLayout extends ViewGroup bottom = top; } if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD) { - mBackgroundBounds.top = (int) Math.max(mTopPadding + mStackTranslation, top); + top = (int) Math.max(mTopPadding + mStackTranslation, top); } else { // otherwise the animation from the shade to the keyguard will jump as it's maxed - mBackgroundBounds.top = Math.max(0, top); + top = Math.max(0, top); } + mBackgroundBounds.top = top; mBackgroundBounds.bottom = Math.min(getHeight(), Math.max(bottom, top)); } @@ -3491,7 +3495,7 @@ public class NotificationStackScrollLayout extends ViewGroup notifyHeightChangeListener(mEmptyShadeView); } }; - if (mAnimationsEnabled) { + if (mAnimationsEnabled && mIsExpanded) { mEmptyShadeView.setWillBeGone(true); mEmptyShadeView.performVisibilityAnimation(false, onFinishedRunnable); } else { diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 8c8b39167718..00707e47e648 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -2270,6 +2270,15 @@ message MetricsEvent { // SUBTYPE: sub settings classname ACTION_SETTING_HELP_AND_FEEDBACK = 513; + // OPEN: Settings > Language & input > Personal dictionary (single locale) + USER_DICTIONARY_SETTINGS = 514; + + // OPEN: Settings > Date & time > Select time zone + ZONE_PICKER = 515; + + // OPEN: Settings > Security > Device administrators + DEVICE_ADMIN_SETTINGS = 516; + // ---- End O Constants, all O constants go above this line ---- // Add new aosp constants above this line. diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index ea25f7438d79..ad87be5e1af9 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -279,6 +279,31 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } @Override + public void onPackageUpdateFinished(String packageName, int uid) { + // Unbind all services from this package, and then update the user state to + // re-bind new versions of them. + synchronized (mLock) { + final int userId = getChangingUserId(); + if (userId != mCurrentUserId) { + return; + } + UserState userState = getUserStateLocked(userId); + boolean unboundAService = false; + for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { + Service boundService = userState.mBoundServices.get(i); + String servicePkg = boundService.mComponentName.getPackageName(); + if (servicePkg.equals(packageName)) { + boundService.unbindLocked(); + unboundAService = true; + } + } + if (unboundAService) { + onUserStateChangedLocked(userState); + } + } + } + + @Override public void onPackageRemoved(String packageName, int uid) { synchronized (mLock) { final int userId = getChangingUserId(); @@ -2281,7 +2306,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public boolean onKeyEvent(KeyEvent keyEvent, int sequenceNumber) { - if (!mRequestFilterKeyEvents) { + if (!mRequestFilterKeyEvents || (mServiceInterface == null)) { return false; } if((mAccessibilityServiceInfo.getCapabilities() @@ -3603,6 +3628,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL: case WindowManager.LayoutParams.TYPE_BASE_APPLICATION: + case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION: case WindowManager.LayoutParams.TYPE_PHONE: case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: case WindowManager.LayoutParams.TYPE_TOAST: diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index b5b0cd86f397..4caeba84b202 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -875,7 +875,7 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_IGNORED; } synchronized (this) { - if (isOpRestricted(uid, code, resolvedPackageName)) { + if (isOpRestrictedLocked(uid, code, resolvedPackageName)) { return AppOpsManager.MODE_IGNORED; } code = AppOpsManager.opToSwitch(code); @@ -1024,7 +1024,7 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_ERRORED; } Op op = getOpLocked(ops, code, true); - if (isOpRestricted(uid, code, packageName)) { + if (isOpRestrictedLocked(uid, code, packageName)) { return AppOpsManager.MODE_IGNORED; } if (op.duration == -1) { @@ -1082,7 +1082,7 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_ERRORED; } Op op = getOpLocked(ops, code, true); - if (isOpRestricted(uid, code, resolvedPackageName)) { + if (isOpRestrictedLocked(uid, code, resolvedPackageName)) { return AppOpsManager.MODE_IGNORED; } final int switchCode = AppOpsManager.opToSwitch(code); @@ -1308,7 +1308,7 @@ public class AppOpsService extends IAppOpsService.Stub { return op; } - private boolean isOpRestricted(int uid, int code, String packageName) { + private boolean isOpRestrictedLocked(int uid, int code, String packageName) { int userHandle = UserHandle.getUserId(uid); final int restrictionSetCount = mOpUserRestrictions.size(); @@ -2210,24 +2210,32 @@ public class AppOpsService extends IAppOpsService.Stub { private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token, int userHandle, String[] exceptionPackages) { - ClientRestrictionState restrictionState = mOpUserRestrictions.get(token); + boolean notifyChange = false; - if (restrictionState == null) { - try { - restrictionState = new ClientRestrictionState(token); - } catch (RemoteException e) { - return; + synchronized (AppOpsService.this) { + ClientRestrictionState restrictionState = mOpUserRestrictions.get(token); + + if (restrictionState == null) { + try { + restrictionState = new ClientRestrictionState(token); + } catch (RemoteException e) { + return; + } + mOpUserRestrictions.put(token, restrictionState); } - mOpUserRestrictions.put(token, restrictionState); - } - if (restrictionState.setRestriction(code, restricted, exceptionPackages, userHandle)) { - notifyWatchersOfChange(code); + if (restrictionState.setRestriction(code, restricted, exceptionPackages, userHandle)) { + notifyChange = true; + } + + if (restrictionState.isDefault()) { + mOpUserRestrictions.remove(token); + restrictionState.destroy(); + } } - if (restrictionState.isDefault()) { - mOpUserRestrictions.remove(token); - restrictionState.destroy(); + if (notifyChange) { + notifyWatchersOfChange(code); } } @@ -2263,10 +2271,12 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void removeUser(int userHandle) throws RemoteException { checkSystemUid("removeUser"); - final int tokenCount = mOpUserRestrictions.size(); - for (int i = tokenCount - 1; i >= 0; i--) { - ClientRestrictionState opRestrictions = mOpUserRestrictions.valueAt(i); - opRestrictions.removeUser(userHandle); + synchronized (AppOpsService.this) { + final int tokenCount = mOpUserRestrictions.size(); + for (int i = tokenCount - 1; i >= 0; i--) { + ClientRestrictionState opRestrictions = mOpUserRestrictions.valueAt(i); + opRestrictions.removeUser(userHandle); + } } } diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 8b243e7f751a..9d880202f7a2 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -28,6 +28,7 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.IBluetoothManagerCallback; import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; +import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -248,8 +249,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext = context; - mPermissionReviewRequired = Build.PERMISSIONS_REVIEW_REQUIRED - || context.getResources().getBoolean( + mPermissionReviewRequired = context.getResources().getBoolean( com.android.internal.R.bool.config_permissionReviewRequired); mBluetooth = null; @@ -742,7 +742,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Legacy apps in permission review mode trigger a user prompt if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { Intent intent = new Intent(intentAction); - mContext.startActivity(intent); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + try { + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + // Shouldn't happen + Slog.e(TAG, "Intent to handle action " + intentAction + " missing"); + return false; + } return true; } } catch (PackageManager.NameNotFoundException e) { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 70f39db72bce..a60078d7d132 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2539,14 +2539,28 @@ public class ConnectivityService extends IConnectivityManager.Stub "request NetworkCapabilities", ConnectivityManager.CALLBACK_CAP_CHANGED); } + private void handleTimedOutNetworkRequest(final NetworkRequestInfo nri) { + if (mNetworkRequests.get(nri.request) != null && mNetworkForRequestId.get( + nri.request.requestId) == null) { + handleRemoveNetworkRequest(nri, ConnectivityManager.CALLBACK_UNAVAIL); + } + } + private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) { final NetworkRequestInfo nri = getNriForAppRequest( request, callingUid, "release NetworkRequest"); - if (nri == null) return; + if (nri != null) { + handleRemoveNetworkRequest(nri, ConnectivityManager.CALLBACK_RELEASED); + } + } - if (VDBG || (DBG && nri.request.isRequest())) log("releasing " + request); + private void handleRemoveNetworkRequest(final NetworkRequestInfo nri, final int whichCallback) { + final String logCallbackType = ConnectivityManager.getCallbackName(whichCallback); + if (VDBG || (DBG && nri.request.isRequest())) { + log("releasing " + nri.request + " (" + logCallbackType + ")"); + } nri.unlinkDeathRecipient(); - mNetworkRequests.remove(request); + mNetworkRequests.remove(nri.request); synchronized (mUidToNetworkRequestCount) { int requests = mUidToNetworkRequestCount.get(nri.mUid, 0); if (requests < 1) { @@ -2635,7 +2649,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } } - callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED, 0); + callCallbackForRequest(nri, null, whichCallback, 0); } @Override @@ -2778,6 +2792,11 @@ public class ConnectivityService extends IConnectivityManager.Stub handleRegisterNetworkRequestWithIntent(msg); break; } + case EVENT_TIMEOUT_NETWORK_REQUEST: { + NetworkRequestInfo nri = (NetworkRequestInfo) msg.obj; + handleTimedOutNetworkRequest(nri); + break; + } case EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT: { handleReleaseNetworkRequestWithIntent((PendingIntent) msg.obj, msg.arg1); break; diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index 801a436e8d80..0cad814fe8b1 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -1223,6 +1223,9 @@ public class LockSettingsService extends ILockSettings.Stub { long challenge, int userId, ICheckCredentialProgressCallback progressCallback) throws RemoteException { checkPasswordReadPermission(userId); + if (TextUtils.isEmpty(pattern)) { + throw new IllegalArgumentException("Pattern can't be null or empty"); + } CredentialHash storedHash = mStorage.readPatternHash(userId); return doVerifyPattern(pattern, storedHash, hasChallenge, challenge, userId, progressCallback); @@ -1324,6 +1327,9 @@ public class LockSettingsService extends ILockSettings.Stub { long challenge, int userId, ICheckCredentialProgressCallback progressCallback) throws RemoteException { checkPasswordReadPermission(userId); + if (TextUtils.isEmpty(password)) { + throw new IllegalArgumentException("Password can't be null or empty"); + } CredentialHash storedHash = mStorage.readPasswordHash(userId); return doVerifyPassword(password, storedHash, hasChallenge, challenge, userId, progressCallback); diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 5a44205edab8..8f165049b431 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -1310,8 +1310,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub mConnector.execute("tether", "interface", "remove", iface); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); + } finally { + removeInterfaceFromLocalNetwork(iface); } - removeInterfaceFromLocalNetwork(iface); } @Override diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 3ebd87cd7b12..fb4b01028e73 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -200,24 +200,24 @@ public final class ActiveServices { switch (msg.what) { case MSG_BG_START_TIMEOUT: { synchronized (mAm) { - rescheduleDelayedStarts(); + rescheduleDelayedStartsLocked(); } } break; } } - void ensureNotStartingBackground(ServiceRecord r) { + void ensureNotStartingBackgroundLocked(ServiceRecord r) { if (mStartingBackground.remove(r)) { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "No longer background starting: " + r); - rescheduleDelayedStarts(); + rescheduleDelayedStartsLocked(); } if (mDelayedStartList.remove(r)) { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "No longer delaying start: " + r); } } - void rescheduleDelayedStarts() { + void rescheduleDelayedStartsLocked() { removeMessages(MSG_BG_START_TIMEOUT); final long now = SystemClock.uptimeMillis(); for (int i=0, N=mStartingBackground.size(); i<N; i++) { @@ -278,19 +278,19 @@ public final class ActiveServices { ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8; } - ServiceRecord getServiceByName(ComponentName name, int callingUser) { + ServiceRecord getServiceByNameLocked(ComponentName name, int callingUser) { // TODO: Deal with global services if (DEBUG_MU) - Slog.v(TAG_MU, "getServiceByName(" + name + "), callingUser = " + callingUser); - return getServiceMap(callingUser).mServicesByName.get(name); + Slog.v(TAG_MU, "getServiceByNameLocked(" + name + "), callingUser = " + callingUser); + return getServiceMapLocked(callingUser).mServicesByName.get(name); } - boolean hasBackgroundServices(int callingUser) { + boolean hasBackgroundServicesLocked(int callingUser) { ServiceMap smap = mServiceMap.get(callingUser); return smap != null ? smap.mStartingBackground.size() >= mMaxStartingBackground : false; } - private ServiceMap getServiceMap(int callingUser) { + private ServiceMap getServiceMapLocked(int callingUser) { ServiceMap smap = mServiceMap.get(callingUser); if (smap == null) { smap = new ServiceMap(mAm.mHandler.getLooper(), callingUser); @@ -299,8 +299,8 @@ public final class ActiveServices { return smap; } - ArrayMap<ComponentName, ServiceRecord> getServices(int callingUser) { - return getServiceMap(callingUser).mServicesByName; + ArrayMap<ComponentName, ServiceRecord> getServicesLocked(int callingUser) { + return getServiceMapLocked(callingUser).mServicesByName; } ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, @@ -368,7 +368,7 @@ public final class ActiveServices { // we do not start the service and launch a review activity if the calling app // is in the foreground passing it a pending intent to start the service when // review is completed. - if (mAm.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) { + if (mAm.mPermissionReviewRequired) { if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage, callingUid, service, callerFg, userId)) { return null; @@ -384,7 +384,7 @@ public final class ActiveServices { r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), service, neededGrants)); - final ServiceMap smap = getServiceMap(r.userId); + final ServiceMap smap = getServiceMapLocked(r.userId); boolean addToStarting = false; if (!callerFg && r.app == null && mAm.mUserController.hasStartedUserState(r.userId)) { @@ -523,10 +523,10 @@ public final class ActiveServices { Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r); } if (first) { - smap.rescheduleDelayedStarts(); + smap.rescheduleDelayedStartsLocked(); } } else if (callerFg) { - smap.ensureNotStartingBackground(r); + smap.ensureNotStartingBackgroundLocked(r); } return r.name; @@ -607,7 +607,7 @@ public final class ActiveServices { for (int i=stopping.size()-1; i>=0; i--) { ServiceRecord service = stopping.get(i); service.delayed = false; - services.ensureNotStartingBackground(service); + services.ensureNotStartingBackgroundLocked(service); stopServiceLocked(service); } } @@ -709,7 +709,7 @@ public final class ActiveServices { if (r.app != null) { updateServiceForegroundLocked(r.app, true); } - getServiceMap(r.userId).ensureNotStartingBackground(r); + getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r); mAm.notifyPackageUse(r.serviceInfo.packageName, PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE); } else { @@ -744,7 +744,7 @@ public final class ActiveServices { // with the same notification ID. If so, we shouldn't actually cancel it, // because that would wipe away the notification that still needs to be shown // due the other service. - ServiceMap sm = getServiceMap(r.userId); + ServiceMap sm = getServiceMapLocked(r.userId); if (sm != null) { for (int i = sm.mServicesByName.size()-1; i >= 0; i--) { ServiceRecord other = sm.mServicesByName.valueAt(i); @@ -912,7 +912,7 @@ public final class ActiveServices { // we schedule binding to the service but do not start its process, then // we launch a review activity to which is passed a callback to invoke // when done to start the bound service's process to completing the binding. - if (mAm.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) { + if (mAm.mPermissionReviewRequired) { if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired( s.packageName, s.userId)) { @@ -1088,7 +1088,7 @@ public final class ActiveServices { requestServiceBindingLocked(s, b.intent, callerFg, false); } - getServiceMap(s.userId).ensureNotStartingBackground(s); + getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s); } finally { Binder.restoreCallingIdentity(origId); @@ -1230,7 +1230,7 @@ public final class ActiveServices { private final ServiceRecord findServiceLocked(ComponentName name, IBinder token, int userId) { - ServiceRecord r = getServiceByName(name, userId); + ServiceRecord r = getServiceByNameLocked(name, userId); return r == token ? r : null; } @@ -1268,7 +1268,7 @@ public final class ActiveServices { userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false, ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null); - ServiceMap smap = getServiceMap(userId); + ServiceMap smap = getServiceMapLocked(userId); final ComponentName comp = service.getComponent(); if (comp != null) { r = smap.mServicesByName.get(comp); @@ -1335,7 +1335,7 @@ public final class ActiveServices { sInfo.name, sInfo.flags) && mAm.isValidSingletonCall(callingUid, sInfo.applicationInfo.uid)) { userId = 0; - smap = getServiceMap(0); + smap = getServiceMapLocked(0); } sInfo = new ServiceInfo(sInfo); sInfo.applicationInfo = mAm.getAppInfoForUser(sInfo.applicationInfo, userId); @@ -1470,8 +1470,7 @@ public final class ActiveServices { return true; } - private final boolean scheduleServiceRestartLocked(ServiceRecord r, - boolean allowCancel) { + private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) { boolean canceled = false; if (mAm.isShuttingDownLocked()) { @@ -1480,7 +1479,7 @@ public final class ActiveServices { return false; } - ServiceMap smap = getServiceMap(r.userId); + ServiceMap smap = getServiceMapLocked(r.userId); if (smap.mServicesByName.get(r.name) != r) { ServiceRecord cur = smap.mServicesByName.get(r.name); Slog.wtf(TAG, "Attempting to schedule restart of " + r @@ -1594,7 +1593,7 @@ public final class ActiveServices { if (!mRestartingServices.contains(r)) { return; } - if (!isServiceNeeded(r, false, false)) { + if (!isServiceNeededLocked(r, false, false)) { // Paranoia: is this service actually needed? In theory a service that is not // needed should never remain on the restart list. In practice... well, there // have been bugs where this happens, and bad things happen because the process @@ -1676,7 +1675,7 @@ public final class ActiveServices { // Make sure this service is no longer considered delayed, we are starting it now. if (r.delayed) { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (bring up): " + r); - getServiceMap(r.userId).mDelayedStartList.remove(r); + getServiceMapLocked(r.userId).mDelayedStartList.remove(r); r.delayed = false; } @@ -1858,7 +1857,7 @@ public final class ActiveServices { if (r.delayed) { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (new proc): " + r); - getServiceMap(r.userId).mDelayedStartList.remove(r); + getServiceMapLocked(r.userId).mDelayedStartList.remove(r); r.delayed = false; } @@ -1939,7 +1938,8 @@ public final class ActiveServices { } } - private final boolean isServiceNeeded(ServiceRecord r, boolean knowConn, boolean hasConn) { + private final boolean isServiceNeededLocked(ServiceRecord r, boolean knowConn, + boolean hasConn) { // Are we still explicitly being asked to run? if (r.startRequested) { return true; @@ -1961,7 +1961,7 @@ public final class ActiveServices { //Slog.i(TAG, "Bring down service:"); //r.dump(" "); - if (isServiceNeeded(r, knowConn, hasConn)) { + if (isServiceNeededLocked(r, knowConn, hasConn)) { return; } @@ -2025,7 +2025,7 @@ public final class ActiveServices { r.userId, System.identityHashCode(r), (r.app != null) ? r.app.pid : -1); } - final ServiceMap smap = getServiceMap(r.userId); + final ServiceMap smap = getServiceMapLocked(r.userId); smap.mServicesByName.remove(r.name); smap.mServicesByIntent.remove(r.intent); r.totalRestartCount = 0; @@ -2097,7 +2097,7 @@ public final class ActiveServices { } } - smap.ensureNotStartingBackground(r); + smap.ensureNotStartingBackgroundLocked(r); } void removeConnectionLocked( @@ -2359,7 +2359,7 @@ public final class ActiveServices { mAm.mProcessStats); realStartServiceLocked(sr, proc, sr.createdFromFg); didSomething = true; - if (!isServiceNeeded(sr, false, false)) { + if (!isServiceNeededLocked(sr, false, false)) { // We were waiting for this service to start, but it is actually no // longer needed. This could happen because bringDownServiceIfNeeded // won't bring down a service that is pending... so now the pending @@ -2480,7 +2480,7 @@ public final class ActiveServices { void cleanUpRemovedTaskLocked(TaskRecord tr, ComponentName component, Intent baseIntent) { ArrayList<ServiceRecord> services = new ArrayList<>(); - ArrayMap<ComponentName, ServiceRecord> alls = getServices(tr.userId); + ArrayMap<ComponentName, ServiceRecord> alls = getServicesLocked(tr.userId); for (int i = alls.size() - 1; i >= 0; i--) { ServiceRecord sr = alls.valueAt(i); if (sr.packageName.equals(component.getPackageName())) { @@ -2612,7 +2612,7 @@ public final class ActiveServices { } } - ServiceMap smap = getServiceMap(app.userId); + ServiceMap smap = getServiceMapLocked(app.userId); // Now do remaining service cleanup. for (int i=app.services.size()-1; i>=0; i--) { @@ -2747,8 +2747,7 @@ public final class ActiveServices { return info; } - List<ActivityManager.RunningServiceInfo> getRunningServiceInfoLocked(int maxNum, - int flags) { + List<ActivityManager.RunningServiceInfo> getRunningServiceInfoLocked(int maxNum, int flags) { ArrayList<ActivityManager.RunningServiceInfo> res = new ArrayList<ActivityManager.RunningServiceInfo>(); @@ -2760,7 +2759,7 @@ public final class ActiveServices { uid) == PackageManager.PERMISSION_GRANTED) { int[] users = mAm.mUserController.getUsers(); for (int ui=0; ui<users.length && res.size() < maxNum; ui++) { - ArrayMap<ComponentName, ServiceRecord> alls = getServices(users[ui]); + ArrayMap<ComponentName, ServiceRecord> alls = getServicesLocked(users[ui]); for (int i=0; i<alls.size() && res.size() < maxNum; i++) { ServiceRecord sr = alls.valueAt(i); res.add(makeRunningServiceInfoLocked(sr)); @@ -2776,7 +2775,7 @@ public final class ActiveServices { } } else { int userId = UserHandle.getUserId(uid); - ArrayMap<ComponentName, ServiceRecord> alls = getServices(userId); + ArrayMap<ComponentName, ServiceRecord> alls = getServicesLocked(userId); for (int i=0; i<alls.size() && res.size() < maxNum; i++) { ServiceRecord sr = alls.valueAt(i); res.add(makeRunningServiceInfoLocked(sr)); @@ -2801,7 +2800,7 @@ public final class ActiveServices { public PendingIntent getRunningServiceControlPanelLocked(ComponentName name) { int userId = UserHandle.getUserId(Binder.getCallingUid()); - ServiceRecord r = getServiceByName(name, userId); + ServiceRecord r = getServiceByNameLocked(name, userId); if (r != null) { for (int conni=r.connections.size()-1; conni>=0; conni--) { ArrayList<ConnectionRecord> conn = r.connections.valueAt(conni); @@ -2874,31 +2873,6 @@ public final class ActiveServices { proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT)); } - /** - * Prints a list of ServiceRecords (dumpsys activity services) - */ - List<ServiceRecord> collectServicesToDumpLocked(ItemMatcher matcher, String dumpPackage) { - final ArrayList<ServiceRecord> services = new ArrayList<>(); - final int[] users = mAm.mUserController.getUsers(); - for (int user : users) { - ServiceMap smap = getServiceMap(user); - if (smap.mServicesByName.size() > 0) { - for (int si=0; si<smap.mServicesByName.size(); si++) { - ServiceRecord r = smap.mServicesByName.valueAt(si); - if (!matcher.match(r, r.name)) { - continue; - } - if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { - continue; - } - services.add(r); - } - } - } - - return services; - } - final class ServiceDumper { private final FileDescriptor fd; private final PrintWriter pw; @@ -2932,7 +2906,7 @@ public final class ActiveServices { final int[] users = mAm.mUserController.getUsers(); for (int user : users) { - ServiceMap smap = getServiceMap(user); + ServiceMap smap = getServiceMapLocked(user); if (smap.mServicesByName.size() > 0) { for (int si=0; si<smap.mServicesByName.size(); si++) { ServiceRecord r = smap.mServicesByName.valueAt(si); @@ -3113,7 +3087,7 @@ public final class ActiveServices { } private void dumpUserRemainsLocked(int user) { - ServiceMap smap = getServiceMap(user); + ServiceMap smap = getServiceMapLocked(user); printed = false; for (int si=0, SN=smap.mDelayedStartList.size(); si<SN; si++) { ServiceRecord r = smap.mDelayedStartList.get(si); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 09a3a172c79f..1c038a0cbae1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1154,6 +1154,7 @@ public final class ActivityManagerService extends ActivityManagerNative * For example, references to the commonly used services. */ HashMap<String, IBinder> mAppBindArgs; + HashMap<String, IBinder> mIsolatedAppBindArgs; /** * Temporary to avoid allocations. Protected by main lock. @@ -2957,18 +2958,24 @@ public final class ActivityManagerService extends ActivityManagerNative * lazily setup to make sure the services are running when they're asked for. */ private HashMap<String, IBinder> getCommonServicesLocked(boolean isolated) { + // Isolated processes won't get this optimization, so that we don't + // violate the rules about which services they have access to. + if (isolated) { + if (mIsolatedAppBindArgs == null) { + mIsolatedAppBindArgs = new HashMap<>(); + mIsolatedAppBindArgs.put("package", ServiceManager.getService("package")); + } + return mIsolatedAppBindArgs; + } + if (mAppBindArgs == null) { mAppBindArgs = new HashMap<>(); - // Isolated processes won't get this optimization, so that we don't - // violate the rules about which services they have access to. - if (!isolated) { - // Setup the application init args - mAppBindArgs.put("package", ServiceManager.getService("package")); - mAppBindArgs.put("window", ServiceManager.getService("window")); - mAppBindArgs.put(Context.ALARM_SERVICE, - ServiceManager.getService(Context.ALARM_SERVICE)); - } + // Setup the application init args + mAppBindArgs.put("package", ServiceManager.getService("package")); + mAppBindArgs.put("window", ServiceManager.getService("window")); + mAppBindArgs.put(Context.ALARM_SERVICE, + ServiceManager.getService(Context.ALARM_SERVICE)); } return mAppBindArgs; } @@ -10766,7 +10773,7 @@ public final class ActivityManagerService extends ActivityManagerNative // If permissions need a review before any of the app components can run, // we return no provider and launch a review activity if the calling app // is in the foreground. - if (mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) { + if (mPermissionReviewRequired) { if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) { return null; } diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 9f875a1f3efa..1816ef49ef08 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -416,8 +416,7 @@ class ActivityStarter { // If permissions need a review before any of the app components can run, we // launch the review activity and pass a pending intent to start the activity // we are to launching now after the review is completed. - if ((mService.mPermissionReviewRequired - || Build.PERMISSIONS_REVIEW_REQUIRED) && aInfo != null) { + if (mService.mPermissionReviewRequired && aInfo != null) { if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired( aInfo.packageName, userId)) { IIntentSender target = mService.getIntentSenderLocked( diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 219e0958a8fe..3b4b8d620a2d 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -434,7 +434,7 @@ public final class BroadcastQueue { // but are on a queue that would like to wait for services to finish before moving // on. If there are background services currently starting, then we will go into a // special state where we hold off on continuing this broadcast until they are done. - if (mService.mServices.hasBackgroundServices(r.userId)) { + if (mService.mServices.hasBackgroundServicesLocked(r.userId)) { Slog.i(TAG, "Delay finish: " + r.curComponent.flattenToShortString()); r.state = BroadcastRecord.WAITING_SERVICES; return false; @@ -625,7 +625,7 @@ public final class BroadcastQueue { // the broadcast and if the calling app is in the foreground and the broadcast is // explicit we launch the review UI passing it a pending intent to send the skipped // broadcast. - if (mService.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) { + if (mService.mPermissionReviewRequired) { if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName, filter.owningUserId)) { r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED; @@ -1131,8 +1131,7 @@ public final class BroadcastQueue { // the broadcast and if the calling app is in the foreground and the broadcast is // explicit we launch the review UI passing it a pending intent to send the skipped // broadcast. - if ((mService.mPermissionReviewRequired - || Build.PERMISSIONS_REVIEW_REQUIRED) && !skip) { + if (mService.mPermissionReviewRequired && !skip) { if (!requestStartTargetPermissionsReviewIfNeededLocked(r, info.activityInfo.packageName, UserHandle.getUserId( info.activityInfo.applicationInfo.uid))) { diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index e0d8373fc445..927f8f98284f 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -1616,6 +1616,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); if (mNotifyList.indexOf(who) < 0) { mNotifyList.add(who); + mIPv6TetheringCoordinator.addActiveDownstream(who); } transitionTo(mTetherModeAliveState); break; @@ -1623,6 +1624,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); mNotifyList.remove(who); + mIPv6TetheringCoordinator.removeActiveDownstream(who); break; default: retValue = false; @@ -1661,17 +1663,19 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering maybeLogMessage(this, message.what); boolean retValue = true; switch (message.what) { - case CMD_TETHER_MODE_REQUESTED: + case CMD_TETHER_MODE_REQUESTED: { TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); if (mNotifyList.indexOf(who) < 0) { mNotifyList.add(who); + mIPv6TetheringCoordinator.addActiveDownstream(who); } who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, mCurrentUpstreamIface); break; - case CMD_TETHER_MODE_UNREQUESTED: - who = (TetherInterfaceStateMachine)message.obj; + } + case CMD_TETHER_MODE_UNREQUESTED: { + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); if (mNotifyList.remove(who)) { if (DBG) Log.d(TAG, "TetherModeAlive removing notifyee " + who); @@ -1689,7 +1693,9 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } else { Log.e(TAG, "TetherModeAliveState UNREQUESTED has unknown who: " + who); } + mIPv6TetheringCoordinator.removeActiveDownstream(who); break; + } case CMD_UPSTREAM_CHANGED: // need to try DUN immediately if Wifi goes down mTryCell = true; diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java index e94b584ac48a..9173febd0ef3 100644 --- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java +++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java @@ -29,6 +29,7 @@ import android.util.Log; import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; +import java.util.LinkedList; /** @@ -45,10 +46,28 @@ public class IPv6TetheringCoordinator { private static final boolean VDBG = false; private final ArrayList<TetherInterfaceStateMachine> mNotifyList; + private final LinkedList<TetherInterfaceStateMachine> mActiveDownstreams; private NetworkState mUpstreamNetworkState; public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList) { mNotifyList = notifyList; + mActiveDownstreams = new LinkedList<>(); + } + + public void addActiveDownstream(TetherInterfaceStateMachine downstream) { + if (mActiveDownstreams.indexOf(downstream) == -1) { + // Adding a new downstream appends it to the list. Adding a + // downstream a second time without first removing it has no effect. + mActiveDownstreams.offer(downstream); + updateIPv6TetheringInterfaces(); + } + } + + public void removeActiveDownstream(TetherInterfaceStateMachine downstream) { + stopIPv6TetheringOn(downstream); + if (mActiveDownstreams.remove(downstream)) { + updateIPv6TetheringInterfaces(); + } } public void updateUpstreamNetworkState(NetworkState ns) { @@ -72,8 +91,7 @@ public class IPv6TetheringCoordinator { private void stopIPv6TetheringOnAllInterfaces() { for (TetherInterfaceStateMachine sm : mNotifyList) { - sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, - 0, 0, null); + stopIPv6TetheringOn(sm); } } @@ -98,28 +116,32 @@ public class IPv6TetheringCoordinator { private void updateIPv6TetheringInterfaces() { for (TetherInterfaceStateMachine sm : mNotifyList) { - final LinkProperties lp = getInterfaceIPv6LinkProperties(sm.interfaceType()); + final LinkProperties lp = getInterfaceIPv6LinkProperties(sm); sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp); break; } } - private LinkProperties getInterfaceIPv6LinkProperties(int interfaceType) { + private LinkProperties getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm) { if (mUpstreamNetworkState == null) return null; + if (sm.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) { + // TODO: Figure out IPv6 support on PAN interfaces. + return null; + } + // NOTE: Here, in future, we would have policies to decide how to divvy // up the available dedicated prefixes among downstream interfaces. // At this time we have no such mechanism--we only support tethering - // IPv6 toward Wi-Fi interfaces. - - switch (interfaceType) { - case ConnectivityManager.TETHERING_WIFI: - final LinkProperties lp = getIPv6OnlyLinkProperties( - mUpstreamNetworkState.linkProperties); - if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) { - return lp; - } - break; + // IPv6 toward the oldest (first requested) active downstream. + + final TetherInterfaceStateMachine currentActive = mActiveDownstreams.peek(); + if (currentActive != null && currentActive == sm) { + final LinkProperties lp = getIPv6OnlyLinkProperties( + mUpstreamNetworkState.linkProperties); + if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) { + return lp; + } } return null; @@ -250,4 +272,8 @@ public class IPv6TetheringCoordinator { ns.networkCapabilities, ns.linkProperties); } + + private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) { + sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null); + } } diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java index edb4347f56a3..7525f302c141 100644 --- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java +++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java @@ -26,6 +26,7 @@ import android.net.RouteInfo; import android.net.ip.RouterAdvertisementDaemon; import android.net.ip.RouterAdvertisementDaemon.RaParams; import android.os.INetworkManagementService; +import android.os.ServiceSpecificException; import android.os.RemoteException; import android.util.Log; import android.util.Slog; @@ -205,7 +206,7 @@ class IPv6TetheringInterfaceServices { final String dnsString = dns.getHostAddress(); try { netd.interfaceDelAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH); - } catch (RemoteException e) { + } catch (ServiceSpecificException | RemoteException e) { Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e); } } @@ -222,7 +223,7 @@ class IPv6TetheringInterfaceServices { final String dnsString = dns.getHostAddress(); try { netd.interfaceAddAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH); - } catch (RemoteException e) { + } catch (ServiceSpecificException | RemoteException e) { Log.e(TAG, "Failed to add local dns IP: " + dnsString, e); newDnses.remove(dns); } @@ -231,7 +232,7 @@ class IPv6TetheringInterfaceServices { try { netd.tetherApplyDnsInterfaces(); - } catch (RemoteException e) { + } catch (ServiceSpecificException | RemoteException e) { Log.e(TAG, "Failed to update local DNS caching server"); if (newDnses != null) newDnses.clear(); } diff --git a/services/core/java/com/android/server/os/SchedulingPolicyService.java b/services/core/java/com/android/server/os/SchedulingPolicyService.java index f98012b39a86..62c9f4c116c2 100644 --- a/services/core/java/com/android/server/os/SchedulingPolicyService.java +++ b/services/core/java/com/android/server/os/SchedulingPolicyService.java @@ -55,7 +55,8 @@ public class SchedulingPolicyService extends ISchedulingPolicyService.Stub { Process.setThreadGroup(tid, Binder.getCallingPid() == pid ? Process.THREAD_GROUP_AUDIO_SYS : Process.THREAD_GROUP_AUDIO_APP); // must be in this order or it fails the schedulability constraint - Process.setThreadScheduler(tid, Process.SCHED_FIFO, prio); + Process.setThreadScheduler(tid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, + prio); } catch (RuntimeException e) { return PackageManager.PERMISSION_DENIED; } diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 87f0581a7879..cec105816b36 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -28,10 +28,13 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; +import android.os.Environment; import android.os.ServiceManager; +import android.os.storage.StorageManager; import android.util.ArraySet; import android.util.Log; +import java.io.File; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.TimeUnit; @@ -66,6 +69,8 @@ public class BackgroundDexOptService extends JobService { */ final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false); + private final File dataDir = Environment.getDataDirectory(); + public static void schedule(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); @@ -113,6 +118,16 @@ public class BackgroundDexOptService extends JobService { return (100 * level / scale); } + private long getLowStorageThreshold() { + @SuppressWarnings("deprecation") + final long lowThreshold = StorageManager.from(this).getStorageLowBytes(dataDir); + if (lowThreshold == 0) { + Log.e(TAG, "Invalid low storage threshold"); + } + + return lowThreshold; + } + private boolean runPostBootUpdate(final JobParameters jobParams, final PackageManagerService pm, final ArraySet<String> pkgs) { if (mExitPostBootUpdate.get()) { @@ -124,6 +139,8 @@ public class BackgroundDexOptService extends JobService { final int lowBatteryThreshold = getResources().getInteger( com.android.internal.R.integer.config_lowBatteryWarningLevel); + final long lowThreshold = getLowStorageThreshold(); + mAbortPostBootUpdate.set(false); new Thread("BackgroundDexOptService_PostBootUpdate") { @Override @@ -141,6 +158,14 @@ public class BackgroundDexOptService extends JobService { // Rather bail than completely drain the battery. break; } + long usableSpace = dataDir.getUsableSpace(); + if (usableSpace < lowThreshold) { + // Rather bail than completely fill up the disk. + Log.w(TAG, "Aborting background dex opt job due to low storage: " + + usableSpace); + break; + } + if (DEBUG_DEXOPT) { Log.i(TAG, "Updating package " + pkg); } @@ -171,6 +196,9 @@ public class BackgroundDexOptService extends JobService { mExitPostBootUpdate.set(true); mAbortIdleOptimization.set(false); + + final long lowThreshold = getLowStorageThreshold(); + new Thread("BackgroundDexOptService_IdleOptimization") { @Override public void run() { @@ -183,6 +211,15 @@ public class BackgroundDexOptService extends JobService { // Skip previously failing package continue; } + + long usableSpace = dataDir.getUsableSpace(); + if (usableSpace < lowThreshold) { + // Rather bail than completely fill up the disk. + Log.w(TAG, "Aborting background dex opt job due to low storage: " + + usableSpace); + break; + } + // Conservatively add package to the list of failing ones in case performDexOpt // never returns. synchronized (sFailedPackageNames) { @@ -213,6 +250,9 @@ public class BackgroundDexOptService extends JobService { Log.i(TAG, "onStartJob"); } + // NOTE: PackageManagerService.isStorageLow uses a different set of criteria from + // the checks above. This check is not "live" - the value is determined by a background + // restart with a period of ~1 minute. PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package"); if (pm.isStorageLow()) { if (DEBUG_DEXOPT) { diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java index 9c9e97e31f29..d1dbdd8b1945 100644 --- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java @@ -17,6 +17,7 @@ package com.android.server.pm; import android.Manifest; +import android.annotation.NonNull; import android.app.DownloadManager; import android.app.admin.DevicePolicyManager; import android.content.Intent; @@ -30,6 +31,9 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Build; +import android.os.Environment; +import android.os.Handler; +import android.os.Message; import android.os.UserHandle; import android.os.storage.StorageManager; import android.print.PrintManager; @@ -39,12 +43,23 @@ import android.provider.MediaStore; import android.provider.Telephony.Sms.Intents; import android.telephony.TelephonyManager; import android.security.Credentials; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.Slog; +import android.util.Xml; +import com.android.internal.util.XmlUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import java.io.BufferedInputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; import static android.os.Process.FIRST_APPLICATION_UID; @@ -65,6 +80,13 @@ final class DefaultPermissionGrantPolicy { private static final String AUDIO_MIME_TYPE = "audio/mpeg"; + private static final String TAG_EXCEPTIONS = "exceptions"; + private static final String TAG_EXCEPTION = "exception"; + private static final String TAG_PERMISSION = "permission"; + private static final String ATTR_PACKAGE = "package"; + private static final String ATTR_NAME = "name"; + private static final String ATTR_FIXED = "fixed"; + private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>(); static { PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE); @@ -126,7 +148,10 @@ final class DefaultPermissionGrantPolicy { STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); } + private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1; + private final PackageManagerService mService; + private final Handler mHandler; private PackagesProvider mLocationPackagesProvider; private PackagesProvider mVoiceInteractionPackagesProvider; @@ -135,8 +160,22 @@ final class DefaultPermissionGrantPolicy { private PackagesProvider mSimCallManagerPackagesProvider; private SyncAdapterPackagesProvider mSyncAdapterPackagesProvider; + private ArrayMap<String, List<DefaultPermissionGrant>> mGrantExceptions; + public DefaultPermissionGrantPolicy(PackageManagerService service) { mService = service; + mHandler = new Handler(mService.mHandlerThread.getLooper()) { + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) { + synchronized (mService.mPackages) { + if (mGrantExceptions == null) { + mGrantExceptions = readDefaultPermissionExceptionsLPw(); + } + } + } + } + }; } public void setLocationPackagesProviderLPw(PackagesProvider provider) { @@ -166,6 +205,11 @@ final class DefaultPermissionGrantPolicy { public void grantDefaultPermissions(int userId) { grantPermissionsToSysComponentsAndPrivApps(userId); grantDefaultSystemHandlerPermissions(userId); + grantDefaultPermissionExceptions(userId); + } + + public void scheduleReadDefaultPermissionExceptions() { + mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS); } private void grantPermissionsToSysComponentsAndPrivApps(int userId) { @@ -916,7 +960,175 @@ final class DefaultPermissionGrantPolicy { pkg.mSignatures) == PackageManager.SIGNATURE_MATCH; } + private void grantDefaultPermissionExceptions(int userId) { + synchronized (mService.mPackages) { + mHandler.removeMessages(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS); + + if (mGrantExceptions == null) { + mGrantExceptions = readDefaultPermissionExceptionsLPw(); + } + + // mGrantExceptions is null only before the first read and then + // it serves as a cache of the default grants that should be + // performed for every user. If there is an entry then the app + // is on the system image and supports runtime permissions. + Set<String> permissions = null; + final int exceptionCount = mGrantExceptions.size(); + for (int i = 0; i < exceptionCount; i++) { + String packageName = mGrantExceptions.keyAt(i); + PackageParser.Package pkg = getSystemPackageLPr(packageName); + List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i); + final int permissionGrantCount = permissionGrants.size(); + for (int j = 0; j < permissionGrantCount; j++) { + DefaultPermissionGrant permissionGrant = permissionGrants.get(j); + if (permissions == null) { + permissions = new ArraySet<>(); + } else { + permissions.clear(); + } + permissions.add(permissionGrant.name); + grantRuntimePermissionsLPw(pkg, permissions, false, + permissionGrant.fixed, userId); + } + } + } + } + + private @NonNull ArrayMap<String, List<DefaultPermissionGrant>> + readDefaultPermissionExceptionsLPw() { + File dir = new File(Environment.getRootDirectory(), "etc/default-permissions"); + if (!dir.exists() || !dir.isDirectory() || !dir.canRead()) { + return new ArrayMap<>(0); + } + + File[] files = dir.listFiles(); + if (files == null) { + return new ArrayMap<>(0); + } + + ArrayMap<String, List<DefaultPermissionGrant>> grantExceptions = new ArrayMap<>(); + + // Iterate over the files in the directory and scan .xml files + for (File file : files) { + if (!file.getPath().endsWith(".xml")) { + Slog.i(TAG, "Non-xml file " + file + " in " + dir + " directory, ignoring"); + continue; + } + if (!file.canRead()) { + Slog.w(TAG, "Default permissions file " + file + " cannot be read"); + continue; + } + try ( + InputStream str = new BufferedInputStream(new FileInputStream(file)) + ) { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(str, null); + parse(parser, grantExceptions); + } catch (XmlPullParserException | IOException e) { + Slog.w(TAG, "Error reading default permissions file " + file, e); + } + } + + return grantExceptions; + } + + private void parse(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>> + outGrantExceptions) throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + if (TAG_EXCEPTIONS.equals(parser.getName())) { + parseExceptions(parser, outGrantExceptions); + } else { + Log.e(TAG, "Unknown tag " + parser.getName()); + } + } + } + + private void parseExceptions(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>> + outGrantExceptions) throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + if (TAG_EXCEPTION.equals(parser.getName())) { + String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); + + List<DefaultPermissionGrant> packageExceptions = + outGrantExceptions.get(packageName); + if (packageExceptions == null) { + // The package must be on the system image + PackageParser.Package pkg = getSystemPackageLPr(packageName); + if (pkg == null) { + Log.w(TAG, "Unknown package:" + packageName); + XmlUtils.skipCurrentTag(parser); + return; + } + + // The package must support runtime permissions + if (!doesPackageSupportRuntimePermissions(pkg)) { + Log.w(TAG, "Skipping non supporting runtime permissions package:" + + packageName); + XmlUtils.skipCurrentTag(parser); + return; + } + packageExceptions = new ArrayList<>(); + outGrantExceptions.put(packageName, packageExceptions); + } + + parsePermission(parser, packageExceptions); + } else { + Log.e(TAG, "Unknown tag " + parser.getName() + "under <exceptions>"); + } + } + } + + private void parsePermission(XmlPullParser parser, List<DefaultPermissionGrant> + outPackageExceptions) throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + if (TAG_PERMISSION.contains(parser.getName())) { + String name = parser.getAttributeValue(null, ATTR_NAME); + if (name == null) { + Log.w(TAG, "Mandatory name attribute missing for permission tag"); + XmlUtils.skipCurrentTag(parser); + continue; + } + + final boolean fixed = XmlUtils.readBooleanAttribute(parser, ATTR_FIXED); + + DefaultPermissionGrant exception = new DefaultPermissionGrant(name, fixed); + outPackageExceptions.add(exception); + } else { + Log.e(TAG, "Unknown tag " + parser.getName() + "under <exception>"); + } + } + } + private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) { return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1; } + + private static final class DefaultPermissionGrant { + final String name; + final boolean fixed; + + public DefaultPermissionGrant(String name, boolean fixed) { + this.name = name; + this.fixed = fixed; + } + } } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 53e328c7ee5f..37c54cfb4899 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -363,7 +363,7 @@ public class LauncherAppsService extends SystemService { private void ensureShortcutPermission(@NonNull String callingPackage, int userId) { verifyCallingPackage(callingPackage); - ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId); + ensureInUserProfiles(userId, "Cannot access shortcuts for unrelated profile " + userId); if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(), callingPackage)) { diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 836b588c7621..bff6d2d4786e 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -93,38 +93,39 @@ public class OtaDexoptService extends IOtaDexopt.Stub { if (mDexoptCommands != null) { throw new IllegalStateException("already called prepare()"); } + final List<PackageParser.Package> important; + final List<PackageParser.Package> others; synchronized (mPackageManagerService.mPackages) { // Important: the packages we need to run with ab-ota compiler-reason. - List<PackageParser.Package> important = PackageManagerServiceUtils.getPackagesForDexopt( + important = PackageManagerServiceUtils.getPackagesForDexopt( mPackageManagerService.mPackages.values(), mPackageManagerService); // Others: we should optimize this with the (first-)boot compiler-reason. - List<PackageParser.Package> others = - new ArrayList<>(mPackageManagerService.mPackages.values()); + 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)); + 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(); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 4994d6bc9206..30b346015388 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -35,7 +35,6 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; import static android.content.pm.PackageManager.INSTALL_EXTERNAL; import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS; import static android.content.pm.PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER; -import static android.content.pm.PackageManager.INSTALL_FAILED_DEXOPT; import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION; import static android.content.pm.PackageManager.INSTALL_FAILED_EPHEMERAL_INVALID; @@ -101,7 +100,6 @@ import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCES import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.IActivityManager; @@ -364,6 +362,7 @@ public class PackageManagerService extends IPackageManager.Stub { private static final boolean DEBUG_TRIAGED_MISSING = false; private static final boolean DEBUG_APP_DATA = false; + /** REMOVE. According to Svet, this was only used to reset permissions during development. */ static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false; private static final boolean DISABLE_EPHEMERAL_APPS = !Build.IS_DEBUGGABLE; @@ -738,8 +737,7 @@ public class PackageManagerService extends IPackageManager.Stub { final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates = new SparseArray<IntentFilterVerificationState>(); - final DefaultPermissionGrantPolicy mDefaultPermissionPolicy = - new DefaultPermissionGrantPolicy(this); + final DefaultPermissionGrantPolicy mDefaultPermissionPolicy; // List of packages names to keep cached, even if they are uninstalled for all users private List<String> mKeepUninstalledPackages; @@ -801,10 +799,9 @@ public class PackageManagerService extends IPackageManager.Stub { PackageParser.ActivityIntentInfo filter = filters.get(m); domainsSet.addAll(filter.getHostsList()); } - ArrayList<String> domainsList = new ArrayList<>(domainsSet); synchronized (mPackages) { if (mSettings.createIntentFilterVerificationIfNeededLPw( - packageName, domainsList) != null) { + packageName, domainsSet) != null) { scheduleWriteSettingsLocked(); } } @@ -2125,6 +2122,8 @@ public class PackageManagerService extends IPackageManager.Stub { mProcessLoggingHandler = new ProcessLoggingHandler(); Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); + mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this); + File dataDir = Environment.getDataDirectory(); mAppInstallDir = new File(dataDir, "app"); mAppLib32InstallDir = new File(dataDir, "app-lib"); @@ -2873,7 +2872,6 @@ public class PackageManagerService extends IPackageManager.Stub { SystemConfig systemConfig = SystemConfig.getInstance(); ArraySet<String> packages = systemConfig.getLinkedApps(); - ArraySet<String> domains = new ArraySet<String>(); for (String packageName : packages) { PackageParser.Package pkg = mPackages.get(packageName); @@ -2883,16 +2881,19 @@ public class PackageManagerService extends IPackageManager.Stub { continue; } - domains.clear(); + ArraySet<String> domains = null; for (PackageParser.Activity a : pkg.activities) { for (ActivityIntentInfo filter : a.intents) { if (hasValidDomains(filter)) { + if (domains == null) { + domains = new ArraySet<String>(); + } domains.addAll(filter.getHostsList()); } } } - if (domains.size() > 0) { + if (domains != null && domains.size() > 0) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.v(TAG, " + " + packageName); } @@ -2900,8 +2901,7 @@ public class PackageManagerService extends IPackageManager.Stub { // state w.r.t. the formal app-linkage "no verification attempted" state; // and then 'always' in the per-user state actually used for intent resolution. final IntentFilterVerificationInfo ivi; - ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName, - new ArrayList<String>(domains)); + ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName, domains); ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED); mSettings.updateIntentFilterVerificationStatusLPw(packageName, INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId); @@ -3997,7 +3997,7 @@ public class PackageManagerService extends IPackageManager.Stub { // their permissions as always granted runtime ones since we need // to keep the review required permission flag per user while an // install permission's state is shared across all users. - if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) + if (mPermissionReviewRequired && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) { return; @@ -4108,7 +4108,7 @@ public class PackageManagerService extends IPackageManager.Stub { // their permissions as always granted runtime ones since we need // to keep the review required permission flag per user while an // install permission's state is shared across all users. - if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) + if (mPermissionReviewRequired && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) { return; @@ -8164,12 +8164,12 @@ public class PackageManagerService extends IPackageManager.Stub { // Just create the setting, don't add it yet. For already existing packages // the PkgSetting exists already and doesn't have to be created. - pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile, - destResourceFile, pkg.applicationInfo.nativeLibraryRootDir, + pkgSetting = mSettings.getPackageWithBenefitsLPw(pkg, origPackage, realName, suid, + destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryRootDir, pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi, pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, - user, false); + user); if (pkgSetting == null) { throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Creating application package " + pkg.packageName + " failed"); @@ -9946,8 +9946,7 @@ public class PackageManagerService extends IPackageManager.Stub { // their permissions as always granted runtime ones since we need // to keep the review required permission flag per user while an // install permission's state is shared across all users. - if (!appSupportsRuntimePermissions && !mPermissionReviewRequired - && !Build.PERMISSIONS_REVIEW_REQUIRED) { + if (!appSupportsRuntimePermissions && !mPermissionReviewRequired) { // For legacy apps dangerous permissions are install time ones. grant = GRANT_INSTALL; } else if (origPermissions.hasInstallPermission(bp.name)) { @@ -10033,7 +10032,7 @@ public class PackageManagerService extends IPackageManager.Stub { changedRuntimePermissionUserIds, userId); } // If the app supports runtime permissions no need for a review. - if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) + if (mPermissionReviewRequired && appSupportsRuntimePermissions && (flags & PackageManager .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { @@ -10042,8 +10041,7 @@ public class PackageManagerService extends IPackageManager.Stub { changedRuntimePermissionUserIds = ArrayUtils.appendInt( changedRuntimePermissionUserIds, userId); } - } else if ((mPermissionReviewRequired - || Build.PERMISSIONS_REVIEW_REQUIRED) + } else if (mPermissionReviewRequired && !appSupportsRuntimePermissions) { // For legacy apps that need a permission review, every new // runtime permission is granted but it is pending a review. @@ -16532,7 +16530,7 @@ public class PackageManagerService extends IPackageManager.Stub { // If permission review is enabled and this is a legacy app, mark the // permission as requiring a review as this is the initial state. int flags = 0; - if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) + if (mPermissionReviewRequired && ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { flags |= FLAG_PERMISSION_REVIEW_REQUIRED; } @@ -18004,6 +18002,13 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); mDefaultPermissionPolicy.grantDefaultPermissions(userId); } + // If we did not grant default permissions, we preload from this the + // default permission exceptions lazily to ensure we don't hit the + // disk on a new user creation. + if (grantPermissionsUserIds == EMPTY_INT_ARRAY) { + mDefaultPermissionPolicy.scheduleReadDefaultPermissionExceptions(); + } + // Kick off any messages waiting for system ready if (mPostSystemReadyMessages != null) { for (Message msg : mPostSystemReadyMessages) { @@ -20407,7 +20412,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); // permissions to keep per user flag state whether review is needed. // Hence, if a new user is added we have to propagate dangerous // permission grants for these legacy apps. - if (mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) { + if (mPermissionReviewRequired) { updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_ALL); } @@ -20861,7 +20866,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); public boolean isPermissionsReviewRequired(String packageName, int userId) { synchronized (mPackages) { // If we do not support permission review, done. - if (!mPermissionReviewRequired && !Build.PERMISSIONS_REVIEW_REQUIRED) { + if (!mPermissionReviewRequired) { return false; } diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index e3866dfada26..a4604a65634c 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -30,15 +30,23 @@ final class PackageSetting extends PackageSettingBase { int appId; PackageParser.Package pkg; SharedUserSetting sharedUser; + /** + * Temporary holding space for the shared user ID. While parsing package settings, the + * shared users tag may be after the packages. In this case, we must delay linking the + * shared user setting with the package setting. The shared user ID lets us link the + * two objects. + */ + private int sharedUserId; PackageSetting(String name, String realName, File codePath, File resourcePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, int pVersionCode, int pkgFlags, int privateFlags, String parentPackageName, - List<String> childPackageNames) { + List<String> childPackageNames, int sharedUserId) { super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode, pkgFlags, privateFlags, parentPackageName, childPackageNames); + this.sharedUserId = sharedUserId; } /** @@ -47,10 +55,14 @@ final class PackageSetting extends PackageSettingBase { */ PackageSetting(PackageSetting orig) { super(orig); + doCopy(orig); + } - appId = orig.appId; - pkg = orig.pkg; - sharedUser = orig.sharedUser; + public int getSharedUserId() { + if (sharedUser != null) { + return sharedUser.userId; + } + return sharedUserId; } @Override @@ -60,6 +72,18 @@ final class PackageSetting extends PackageSettingBase { + " " + name + "/" + appId + "}"; } + public void copyFrom(PackageSetting orig) { + super.copyFrom(orig); + doCopy(orig); + } + + private void doCopy(PackageSetting orig) { + appId = orig.appId; + pkg = orig.pkg; + sharedUser = orig.sharedUser; + sharedUserId = orig.sharedUserId; + } + public PermissionsState getPermissionsState() { return (sharedUser != null) ? sharedUser.getPermissionsState() diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 14ca72c14584..c08bc5743b57 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -147,51 +147,12 @@ abstract class PackageSettingBase extends SettingBase { secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode); } - /** - * New instance of PackageSetting with one-level-deep cloning. - */ - @SuppressWarnings("unchecked") + /** New instance of PackageSetting with one-level-deep cloning. */ PackageSettingBase(PackageSettingBase base) { super(base); - name = base.name; realName = base.realName; - codePath = base.codePath; - codePathString = base.codePathString; - resourcePath = base.resourcePath; - resourcePathString = base.resourcePathString; - legacyNativeLibraryPathString = base.legacyNativeLibraryPathString; - primaryCpuAbiString = base.primaryCpuAbiString; - secondaryCpuAbiString = base.secondaryCpuAbiString; - cpuAbiOverrideString = base.cpuAbiOverrideString; - timeStamp = base.timeStamp; - firstInstallTime = base.firstInstallTime; - lastUpdateTime = base.lastUpdateTime; - versionCode = base.versionCode; - - uidError = base.uidError; - - signatures = new PackageSignatures(base.signatures); - - installPermissionsFixed = base.installPermissionsFixed; - userState.clear(); - for (int i=0; i<base.userState.size(); i++) { - userState.put(base.userState.keyAt(i), - new PackageUserState(base.userState.valueAt(i))); - } - installStatus = base.installStatus; - - origPackage = base.origPackage; - - installerPackageName = base.installerPackageName; - isOrphaned = base.isOrphaned; - volumeUuid = base.volumeUuid; - - keySetData = new PackageKeySetData(base.keySetData); - - parentPackageName = base.parentPackageName; - childPackageNames = (base.childPackageNames != null) - ? new ArrayList<>(base.childPackageNames) : null; + doCopy(base); } void init(File codePath, File resourcePath, String legacyNativeLibraryPathString, @@ -237,27 +198,47 @@ abstract class PackageSettingBase extends SettingBase { } /** - * Make a shallow copy of this package settings. + * Makes a shallow copy of the given package settings. + * + * NOTE: For some fields [such as keySetData, signatures, userState, verificationInfo, etc...], + * the original object is copied and a new one is not created. */ - public void copyFrom(PackageSettingBase base) { - mPermissionsState.copyFrom(base.mPermissionsState); - primaryCpuAbiString = base.primaryCpuAbiString; - secondaryCpuAbiString = base.secondaryCpuAbiString; - cpuAbiOverrideString = base.cpuAbiOverrideString; - timeStamp = base.timeStamp; - firstInstallTime = base.firstInstallTime; - lastUpdateTime = base.lastUpdateTime; - signatures = base.signatures; - installPermissionsFixed = base.installPermissionsFixed; + public void copyFrom(PackageSettingBase orig) { + super.copyFrom(orig); + doCopy(orig); + } + + private void doCopy(PackageSettingBase orig) { + childPackageNames = (orig.childPackageNames != null) + ? new ArrayList<>(orig.childPackageNames) : null; + codePath = orig.codePath; + codePathString = orig.codePathString; + cpuAbiOverrideString = orig.cpuAbiOverrideString; + firstInstallTime = orig.firstInstallTime; + installPermissionsFixed = orig.installPermissionsFixed; + installStatus = orig.installStatus; + installerPackageName = orig.installerPackageName; + isOrphaned = orig.isOrphaned; + keySetData = orig.keySetData; + lastUpdateTime = orig.lastUpdateTime; + legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString; + // Intentionally skip oldCodePaths; it's not relevant for copies + origPackage = orig.origPackage; + parentPackageName = orig.parentPackageName; + primaryCpuAbiString = orig.primaryCpuAbiString; + resourcePath = orig.resourcePath; + resourcePathString = orig.resourcePathString; + secondaryCpuAbiString = orig.secondaryCpuAbiString; + signatures = orig.signatures; + timeStamp = orig.timeStamp; + uidError = orig.uidError; userState.clear(); - for (int i=0; i<base.userState.size(); i++) { - userState.put(base.userState.keyAt(i), base.userState.valueAt(i)); + for (int i=0; i<orig.userState.size(); i++) { + userState.put(orig.userState.keyAt(i), orig.userState.valueAt(i)); } - installStatus = base.installStatus; - keySetData = base.keySetData; - verificationInfo = base.verificationInfo; - installerPackageName = base.installerPackageName; - volumeUuid = base.volumeUuid; + verificationInfo = orig.verificationInfo; + versionCode = orig.versionCode; + volumeUuid = orig.volumeUuid; } private PackageUserState modifyUserState(int userId) { diff --git a/services/core/java/com/android/server/pm/PendingPackage.java b/services/core/java/com/android/server/pm/PendingPackage.java deleted file mode 100644 index da73085985ba..000000000000 --- a/services/core/java/com/android/server/pm/PendingPackage.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2011 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 java.io.File; -import java.util.List; - -final class PendingPackage extends PackageSettingBase { - final int sharedId; - - PendingPackage(String name, String realName, File codePath, File resourcePath, - String legacyNativeLibraryPathString, String primaryCpuAbiString, - String secondaryCpuAbiString, String cpuAbiOverrideString, int sharedId, - int pVersionCode, int pkgFlags, int pkgPrivateFlags, String parentPackageName, - List<String> childPackageNames) { - super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString, - primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, - pVersionCode, pkgFlags, pkgPrivateFlags, parentPackageName, childPackageNames); - this.sharedId = sharedId; - } -} diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java index 2921032b5572..71e8d515444b 100644 --- a/services/core/java/com/android/server/pm/SettingBase.java +++ b/services/core/java/com/android/server/pm/SettingBase.java @@ -30,10 +30,19 @@ abstract class SettingBase { mPermissionsState = new PermissionsState(); } - SettingBase(SettingBase base) { - pkgFlags = base.pkgFlags; - pkgPrivateFlags = base.pkgPrivateFlags; - mPermissionsState = new PermissionsState(base.mPermissionsState); + SettingBase(SettingBase orig) { + mPermissionsState = new PermissionsState(); + doCopy(orig); + } + + public void copyFrom(SettingBase orig) { + doCopy(orig); + } + + private void doCopy(SettingBase orig) { + pkgFlags = orig.pkgFlags; + pkgPrivateFlags = orig.pkgPrivateFlags; + mPermissionsState.copyFrom(orig.mPermissionsState); } public PermissionsState getPermissionsState() { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 31a1c36c7fd5..52313b1ce262 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -166,6 +166,7 @@ final class Settings { private static final boolean DEBUG_STOPPED = false; private static final boolean DEBUG_MU = false; private static final boolean DEBUG_KERNEL = false; + private static final boolean DEBUG_PARSER = false; private static final String RUNTIME_PERMISSIONS_FILE_NAME = "runtime-permissions.xml"; @@ -393,7 +394,7 @@ final class Settings { * TODO: make this just a local variable that is passed in during package * scanning to make it less confusing. */ - private final ArrayList<PendingPackage> mPendingPackages = new ArrayList<PendingPackage>(); + private final ArrayList<PackageSetting> mPendingPackages = new ArrayList<>(); private final File mSystemDir; @@ -427,18 +428,22 @@ final class Settings { mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml"); } - PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage, + PackageSetting getPackageLPw(String pkgName) { + return peekPackageLPr(pkgName); + } + + PackageSetting getPackageWithBenefitsLPw(PackageParser.Package pkg, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, String legacyNativeLibraryPathString, String primaryCpuAbi, String secondaryCpuAbi, - int pkgFlags, int pkgPrivateFlags, UserHandle user, boolean add) + int pkgFlags, int pkgPrivateFlags, UserHandle user) throws PackageManagerException { final String name = pkg.packageName; final String parentPackageName = (pkg.parentPackage != null) ? pkg.parentPackage.packageName : null; - PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath, - resourcePath, legacyNativeLibraryPathString, primaryCpuAbi, secondaryCpuAbi, - pkg.mVersionCode, pkgFlags, pkgPrivateFlags, user, add, true /*allowInstall*/, - parentPackageName, pkg.getChildPackageNames()); + PackageSetting p = getPackageWithBenefitsLPw(name, origPackage, realName, sharedUser, + codePath, resourcePath, legacyNativeLibraryPathString, primaryCpuAbi, + secondaryCpuAbi, pkg.mVersionCode, pkgFlags, pkgPrivateFlags, user, + true /*allowInstall*/, parentPackageName, pkg.getChildPackageNames()); return p; } @@ -602,7 +607,7 @@ final class Settings { p = new PackageSetting(name, realName, codePath, resourcePath, legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, parentPackageName, - childPackageNames); + childPackageNames, 0 /*userId*/); p.appId = uid; if (addUserIdLPw(uid, p, name)) { mPackages.put(name, p); @@ -679,11 +684,11 @@ final class Settings { } } - private PackageSetting getPackageLPw(String name, PackageSetting origPackage, + private PackageSetting getPackageWithBenefitsLPw(String name, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, int vc, int pkgFlags, int pkgPrivateFlags, - UserHandle installUser, boolean add, boolean allowInstall, String parentPackage, + UserHandle installUser, boolean allowInstall, String parentPackage, List<String> childPackageNames) throws PackageManagerException { final UserManagerService userManager = UserManagerService.getInstance(); final PackageSetting disabledPackage = getDisabledSystemPkgLPr(name); @@ -714,11 +719,6 @@ final class Settings { throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Creating application package " + name + " failed"); } - if (add) { - // Finish adding new package by adding it and updating shared - // user preferences - addPackageSettingLPw(p, name, sharedUser); - } } if (peekPackageLPr(name) != null) { final List<UserInfo> allUsers = getAllUsers(UserManagerService.getInstance()); @@ -857,7 +857,7 @@ final class Settings { pkgSetting = new PackageSetting(originalPkg.name, pkgName, codePath, resourcePath, legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi, null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags, - parentPkgName, childPkgNames); + parentPkgName, childPkgNames, 0 /*sharedUserId*/); if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " + pkgName + " is adopting original package " + originalPkg.name); // Note that we will retain the new package's signature so @@ -876,7 +876,7 @@ final class Settings { pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, resourcePath, legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi, null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags, - parentPkgName, childPkgNames); + parentPkgName, childPkgNames, 0 /*sharedUserId*/); pkgSetting.setTimeStamp(codePath.lastModified()); pkgSetting.sharedUser = sharedUser; // If this is not a system app, it starts out stopped. @@ -1025,15 +1025,14 @@ final class Settings { if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) { p.sharedUser.signatures.assignSignatures(pkg.mSignatures); } - addPackageSettingLPw(p, pkg.packageName, p.sharedUser); + addPackageSettingLPw(p, p.sharedUser); } // Utility method that adds a PackageSetting to mPackages and // completes updating the shared user attributes and any restored // app link verification state - private void addPackageSettingLPw(PackageSetting p, String name, - SharedUserSetting sharedUser) { - mPackages.put(name, p); + private void addPackageSettingLPw(PackageSetting p, SharedUserSetting sharedUser) { + mPackages.put(p.name, p); if (sharedUser != null) { if (p.sharedUser != null && p.sharedUser != sharedUser) { PackageManagerService.reportSettingsProblem(Log.ERROR, @@ -1067,12 +1066,12 @@ final class Settings { } } - IntentFilterVerificationInfo ivi = mRestoredIntentFilterVerifications.get(name); + IntentFilterVerificationInfo ivi = mRestoredIntentFilterVerifications.get(p.name); if (ivi != null) { if (DEBUG_DOMAIN_VERIFICATION) { - Slog.i(TAG, "Applying restored IVI for " + name + " : " + ivi.getStatusString()); + Slog.i(TAG, "Applying restored IVI for " + p.name + " : " + ivi.getStatusString()); } - mRestoredIntentFilterVerifications.remove(name); + mRestoredIntentFilterVerifications.remove(p.name); p.setIntentFilterVerificationInfo(ivi); } } @@ -1320,7 +1319,7 @@ final class Settings { /* package protected */ IntentFilterVerificationInfo createIntentFilterVerificationIfNeededLPw(String packageName, - ArrayList<String> domains) { + ArraySet<String> domains) { PackageSetting ps = mPackages.get(packageName); if (ps == null) { if (DEBUG_DOMAIN_VERIFICATION) { @@ -1595,7 +1594,9 @@ final class Settings { throws XmlPullParserException, IOException { IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser); packageSetting.setIntentFilterVerificationInfo(ivi); - Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName()); + if (DEBUG_PARSER) { + Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName()); + } } private void readRestoredIntentFilterVerifications(XmlPullParser parser) @@ -3039,30 +3040,22 @@ final class Settings { final int N = mPendingPackages.size(); for (int i = 0; i < N; i++) { - final PendingPackage pp = mPendingPackages.get(i); - Object idObj = getUserIdLPr(pp.sharedId); - if (idObj != null && idObj instanceof SharedUserSetting) { - try { - PackageSetting p = getPackageLPw(pp.name, null, pp.realName, - (SharedUserSetting) idObj, pp.codePath, pp.resourcePath, - pp.legacyNativeLibraryPathString, pp.primaryCpuAbiString, - pp.secondaryCpuAbiString, pp.versionCode, pp.pkgFlags, - pp.pkgPrivateFlags, null /*installUser*/, true /*add*/, - false /*allowInstall*/, pp.parentPackageName, pp.childPackageNames); - p.copyFrom(pp); - } catch (PackageManagerException e) { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Unable to create application package for " + pp.name); - continue; - } + final PackageSetting p = mPendingPackages.get(i); + final int sharedUserId = p.getSharedUserId(); + final Object idObj = getUserIdLPr(sharedUserId); + if (idObj instanceof SharedUserSetting) { + final SharedUserSetting sharedUser = (SharedUserSetting) idObj; + p.sharedUser = sharedUser; + p.appId = sharedUser.userId; + addPackageSettingLPw(p, sharedUser); } else if (idObj != null) { - String msg = "Bad package setting: package " + pp.name + " has shared uid " - + pp.sharedId + " that is not a shared uid\n"; + String msg = "Bad package setting: package " + p.name + " has shared uid " + + sharedUserId + " that is not a shared uid\n"; mReadMessages.append(msg); PackageManagerService.reportSettingsProblem(Log.ERROR, msg); } else { - String msg = "Bad package setting: package " + pp.name + " has shared uid " - + pp.sharedId + " that is not defined\n"; + String msg = "Bad package setting: package " + p.name + " has shared uid " + + sharedUserId + " that is not defined\n"; mReadMessages.append(msg); PackageManagerService.reportSettingsProblem(Log.ERROR, msg); } @@ -3535,7 +3528,7 @@ final class Settings { PackageSetting ps = new PackageSetting(name, realName, codePathFile, new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr, secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags, - parentPackageName, null); + parentPackageName, null /*childPackageNames*/, 0 /*sharedUserId*/); String timeStampStr = parser.getAttributeValue(null, "ft"); if (timeStampStr != null) { try { @@ -3627,7 +3620,7 @@ final class Settings { long timeStamp = 0; long firstInstallTime = 0; long lastUpdateTime = 0; - PackageSettingBase packageSetting = null; + PackageSetting packageSetting = null; String version = null; int versionCode = 0; String parentPackageName; @@ -3746,7 +3739,8 @@ final class Settings { if (PackageManagerService.DEBUG_SETTINGS) Log.v(PackageManagerService.TAG, "Reading package: " + name + " userId=" + idStr + " sharedUserId=" + sharedIdStr); - int userId = idStr != null ? Integer.parseInt(idStr) : 0; + final int userId = idStr != null ? Integer.parseInt(idStr) : 0; + final int sharedUserId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; if (resourcePathStr == null) { resourcePathStr = codePathStr; } @@ -3765,7 +3759,7 @@ final class Settings { packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags, - pkgPrivateFlags, parentPackageName, null); + pkgPrivateFlags, parentPackageName, null /*childPackageNames*/); if (PackageManagerService.DEBUG_SETTINGS) Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId=" + userId + " pkg=" + packageSetting); @@ -3779,20 +3773,19 @@ final class Settings { packageSetting.lastUpdateTime = lastUpdateTime; } } else if (sharedIdStr != null) { - userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; - if (userId > 0) { - packageSetting = new PendingPackage(name.intern(), realName, new File( + if (sharedUserId > 0) { + packageSetting = new PackageSetting(name.intern(), realName, new File( codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, - userId, versionCode, pkgFlags, pkgPrivateFlags, parentPackageName, - null); + versionCode, pkgFlags, pkgPrivateFlags, parentPackageName, + null /*childPackageNames*/, sharedUserId); packageSetting.setTimeStamp(timeStamp); packageSetting.firstInstallTime = firstInstallTime; packageSetting.lastUpdateTime = lastUpdateTime; - mPendingPackages.add((PendingPackage) packageSetting); + mPendingPackages.add(packageSetting); if (PackageManagerService.DEBUG_SETTINGS) Log.i(PackageManagerService.TAG, "Reading package " + name - + ": sharedUserId=" + userId + " pkg=" + packageSetting); + + ": sharedUserId=" + sharedUserId + " pkg=" + packageSetting); } else { PackageManagerService.reportSettingsProblem(Log.WARN, "Error in package manager settings: package " + name diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 9ce147d29d63..37a8389def62 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2972,6 +2972,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } switch (animationHint) { case ROTATION_ANIMATION_CROSSFADE: + case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless. anim[0] = R.anim.rotation_animation_xfade_exit; anim[1] = R.anim.rotation_animation_enter; break; @@ -5400,6 +5401,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mKeyguardOccluded = false; mKeyguardDelegate.setOccluded(false); mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD; + if (!mKeyguardDelegate.hasLockscreenWallpaper()) { + mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER; + } return true; } else if (!wasOccluded && isOccluded && showing) { mKeyguardOccluded = true; @@ -7829,14 +7833,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { return false; } + final WindowState w = mTopFullscreenOpaqueWindowState; + // We only enable seamless rotation if the top window has requested // it and is in the fullscreen opaque state. Seamless rotation // requires freezing various Surface states and won't work well // with animations, so we disable it in the animation case for now. - if (mTopFullscreenOpaqueWindowState != null && mTopIsFullscreen && - !mTopFullscreenOpaqueWindowState.isAnimatingLw() && - mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation == - ROTATION_ANIMATION_JUMPCUT) { + if (w != null && !w.isAnimatingLw() && + ((w.getAttrs().rotationAnimation == ROTATION_ANIMATION_JUMPCUT) || + (w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS))) { return true; } return false; diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java index ef6d92f3a773..e4a9254958df 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java @@ -210,6 +210,13 @@ public class KeyguardServiceDelegate { return false; } + public boolean hasLockscreenWallpaper() { + if (mKeyguardService != null) { + return mKeyguardService.hasLockscreenWallpaper(); + } + return false; + } + public boolean isInputRestricted() { if (mKeyguardService != null) { mKeyguardState.inputRestricted = mKeyguardService.isInputRestricted(); diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java index 0274cb5763bf..5a4356876447 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java @@ -247,6 +247,10 @@ public class KeyguardServiceWrapper implements IKeyguardService { return mKeyguardStateMonitor.isTrusted(); } + public boolean hasLockscreenWallpaper() { + return mKeyguardStateMonitor.hasLockscreenWallpaper(); + } + public boolean isSecure(int userId) { return mKeyguardStateMonitor.isSecure(userId); } diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java index f3238c824104..08eaaa908d08 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java @@ -44,6 +44,7 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { private volatile boolean mSimSecure = true; private volatile boolean mInputRestricted = true; private volatile boolean mTrusted = false; + private volatile boolean mHasLockscreenWallpaper = false; private int mCurrentUserId; @@ -75,6 +76,10 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { return mTrusted; } + public boolean hasLockscreenWallpaper() { + return mHasLockscreenWallpaper; + } + @Override // Binder interface public void onShowingStateChanged(boolean showing) { mIsShowing = showing; @@ -103,6 +108,11 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { mTrusted = trusted; } + @Override // Binder interface + public void onHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper) { + mHasLockscreenWallpaper = hasLockscreenWallpaper; + } + public void dump(String prefix, PrintWriter pw) { pw.println(prefix + TAG); prefix += " "; diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 216afe610261..d219aedf5fe7 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -102,9 +102,8 @@ public class TrustManagerService extends SystemService { private static final int MSG_START_USER = 7; private static final int MSG_CLEANUP_USER = 8; private static final int MSG_SWITCH_USER = 9; - private static final int MSG_SET_DEVICE_LOCKED = 10; - private static final int MSG_FLUSH_TRUST_USUALLY_MANAGED = 11; - private static final int MSG_UNLOCK_USER = 12; + private static final int MSG_FLUSH_TRUST_USUALLY_MANAGED = 10; + private static final int MSG_UNLOCK_USER = 11; private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000; @@ -352,20 +351,6 @@ public class TrustManagerService extends SystemService { } } - public void setDeviceLockedForUser(int userId, boolean locked) { - if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { - synchronized (mDeviceLockedForUser) { - mDeviceLockedForUser.put(userId, locked); - } - if (locked) { - try { - ActivityManagerNative.getDefault().notifyLockedProfile(userId); - } catch (RemoteException e) { - } - } - } - } - boolean isDeviceLockedInner(int userId) { synchronized (mDeviceLockedForUser) { return mDeviceLockedForUser.get(userId, true); @@ -873,10 +858,24 @@ public class TrustManagerService extends SystemService { } @Override - public void setDeviceLockedForUser(int userId, boolean value) { + public void setDeviceLockedForUser(int userId, boolean locked) { enforceReportPermission(); - mHandler.obtainMessage(MSG_SET_DEVICE_LOCKED, value ? 1 : 0, userId) - .sendToTarget(); + final long identity = Binder.clearCallingIdentity(); + try { + if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { + synchronized (mDeviceLockedForUser) { + mDeviceLockedForUser.put(userId, locked); + } + if (locked) { + try { + ActivityManagerNative.getDefault().notifyLockedProfile(userId); + } catch (RemoteException e) { + } + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } } @Override @@ -952,9 +951,6 @@ public class TrustManagerService extends SystemService { mCurrentUser = msg.arg1; refreshDeviceLockedForUser(UserHandle.USER_ALL); break; - case MSG_SET_DEVICE_LOCKED: - setDeviceLockedForUser(msg.arg2, msg.arg1 != 0); - break; case MSG_FLUSH_TRUST_USUALLY_MANAGED: SparseBooleanArray usuallyManaged; synchronized (mTrustUsuallyManagedForUser) { diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 2d60f437ea3a..0605d8073301 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -358,6 +358,7 @@ final class AccessibilityController { } switch (type) { case WindowManager.LayoutParams.TYPE_APPLICATION: + case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION: case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index d3dab440c44a..e62d810e9d4f 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -498,6 +498,7 @@ class AppWindowToken extends WindowToken { } void clearAnimatingFlags() { + boolean wallpaperMightChange = false; for (int i = windows.size() - 1; i >= 0; i--) { final WindowState win = windows.get(i); // We don't want to clear it out for windows that get replaced, because the @@ -508,7 +509,6 @@ class AppWindowToken extends WindowToken { // by the client. We should let animation proceed and not clear this flag or // they won't eventually be removed by WindowStateAnimator#finishExit. if (!win.mWillReplaceWindow && !win.mRemoveOnExit) { - win.mAnimatingExit = false; // Clear mAnimating flag together with mAnimatingExit. When animation // changes from exiting to entering, we need to clear this flag until the // new animation gets applied, so that isAnimationStarting() becomes true @@ -516,15 +516,24 @@ class AppWindowToken extends WindowToken { // Otherwise applySurfaceChangesTransaction will faill to skip surface // placement for this window during this period, one or more frame will // show up with wrong position or scale. - win.mWinAnimator.mAnimating = false; - + if (win.mAnimatingExit) { + win.mAnimatingExit = false; + wallpaperMightChange = true; + } + if (win.mWinAnimator.mAnimating) { + win.mWinAnimator.mAnimating = false; + wallpaperMightChange = true; + } if (win.mDestroying) { win.mDestroying = false; mService.mDestroySurface.remove(win); + wallpaperMightChange = true; } } } - requestUpdateWallpaperIfNeeded(); + if (wallpaperMightChange) { + requestUpdateWallpaperIfNeeded(); + } } void destroySurfaces() { @@ -1015,8 +1024,8 @@ class AppWindowToken extends WindowToken { // // As we use this flag as a hint to freeze surface boundary updates, // we'd like to only apply this to TYPE_BASE_APPLICATION, - // windows of TYPE_APPLICATION like dialogs, could appear - // to not be drag resizing while they resize, but we'd + // windows of TYPE_APPLICATION (or TYPE_DRAWN_APPLICATION) like dialogs, + // could appear to not be drag resizing while they resize, but we'd // still like to manipulate their frame to update crop, etc... // // Anyway we don't need to synchronize position and content updates for these diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index db6ff7139360..b269d4c76c05 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -508,9 +508,14 @@ class WallpaperController { if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "New i: " + wallpaperTargetIndex + " old i: " + oldI); if (oldI >= 0) { - if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, - "Animating wallpapers: old#" + oldI + "=" + oldW + "; new#" - + wallpaperTargetIndex + "=" + wallpaperTarget); + final boolean newTargetHidden = + wallpaperTarget.mAppToken != null && wallpaperTarget.mAppToken.hiddenRequested; + final boolean oldTargetHidden = + oldW.mAppToken != null && oldW.mAppToken.hiddenRequested; + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers:" + + " old#" + oldI + "=" + oldW + " hidden=" + oldTargetHidden + + " new#" + wallpaperTargetIndex + "=" + wallpaperTarget + + " hidden=" + newTargetHidden); // Set the upper and lower wallpaper targets correctly, // and make sure that we are positioning the wallpaper below the lower. @@ -520,6 +525,7 @@ class WallpaperController { "Found target above old target."); mUpperWallpaperTarget = wallpaperTarget; mLowerWallpaperTarget = oldW; + wallpaperTarget = oldW; wallpaperTargetIndex = oldI; } else { @@ -529,15 +535,21 @@ class WallpaperController { mUpperWallpaperTarget = oldW; mLowerWallpaperTarget = wallpaperTarget; } - - // If the new target is going hidden, set it back to the old target. - if (wallpaperTarget.mAppToken != null - && wallpaperTarget.mAppToken.hiddenRequested) { + if (newTargetHidden && !oldTargetHidden) { if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Old wallpaper still the target."); + // Use the old target if new target is hidden but old target + // is not. If they're both hidden, still use the new target. + mWallpaperTarget = oldW; + } else if (newTargetHidden == oldTargetHidden + && !mService.mOpeningApps.contains(wallpaperTarget.mAppToken) + && (mService.mOpeningApps.contains(oldW.mAppToken) + || mService.mClosingApps.contains(oldW.mAppToken))) { + // If they're both hidden (or both not hidden), prefer the one that's + // currently in opening or closing app list, this allows transition + // selection logic to better determine the wallpaper status of + // opening/closing apps. mWallpaperTarget = oldW; - wallpaperTarget = oldW; - wallpaperTargetIndex = oldI; } } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 22120d016af4..27dd40fa0b42 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -192,6 +192,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; @@ -4890,7 +4891,8 @@ public class WindowManagerService extends IWindowManager.Stub if (w.isDrawnLw()) { if (w.mAttrs.type == TYPE_BOOT_PROGRESS) { haveBootMsg = true; - } else if (w.mAttrs.type == TYPE_APPLICATION) { + } else if (w.mAttrs.type == TYPE_APPLICATION + || w.mAttrs.type == TYPE_DRAWN_APPLICATION) { haveApp = true; } else if (w.mAttrs.type == TYPE_WALLPAPER) { haveWallpaper = true; @@ -5776,6 +5778,20 @@ public class WindowManagerService extends IWindowManager.Stub rotateSeamlessly = false; break; } + // In what can only be called an unfortunate workaround we require + // seamlessly rotated child windows to have the TRANSFORM_TO_DISPLAY_INVERSE + // flag. Due to limitations in the client API, there is no way for + // the client to set this flag in a race free fashion. If we seamlessly rotate + // a window which does not have this flag, but then gains it, we will get + // an incorrect visual result (rotated viewfinder). This means if we want to + // support seamlessly rotating windows which could gain this flag, we can't + // rotate windows without it. This limits seamless rotation in N to camera framework + // users, windows without children, and native code. This is unfortunate but + // having the camera work is our primary goal. + if (w.isChildWindow() & w.isVisibleNow() && + !w.mWinAnimator.mSurfaceController.getTransformToDisplayInverse()) { + rotateSeamlessly = false; + } } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 546498a9a4bd..cb8660b537dd 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -93,6 +93,7 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; @@ -1280,7 +1281,8 @@ class WindowState extends WindowContainer implements WindowManagerPolicy.WindowS final boolean isViewVisible = (mAppToken == null || !mAppToken.clientHidden) && (mViewVisibility == View.VISIBLE) && !mWindowRemovalAllowed; return (isOnScreenIgnoringKeyguard() && (!visibleOnly || isViewVisible) - || mWinAnimator.mAttrType == TYPE_BASE_APPLICATION) + || mWinAnimator.mAttrType == TYPE_BASE_APPLICATION + || mWinAnimator.mAttrType == TYPE_DRAWN_APPLICATION) && !mAnimatingExit && !mDestroying; } @@ -1325,7 +1327,8 @@ class WindowState extends WindowContainer implements WindowManagerPolicy.WindowS && ((!isParentWindowHidden() && mViewVisibility == View.VISIBLE && !mToken.hidden) || mWinAnimator.mAnimation != null || ((atoken != null) && (atoken.mAppAnimator.animation != null) - && !mWinAnimator.isDummyAnimation())); + && !mWinAnimator.isDummyAnimation()) + || isAnimatingWithSavedSurface()); } /** @@ -2271,6 +2274,8 @@ class WindowState extends WindowContainer implements WindowManagerPolicy.WindowS mWinAnimator.mDrawState = READY_TO_SHOW; mAnimatingWithSavedSurface = true; + requestUpdateWallpaperIfNeeded(); + if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { Slog.v(TAG, "Restoring saved surface: " + this); } @@ -3089,12 +3094,13 @@ class WindowState extends WindowContainer implements WindowManagerPolicy.WindowS // for only child windows (as the main window is handled by window preservation) // and the big surface. // - // Though windows of TYPE_APPLICATION (as opposed to TYPE_BASE_APPLICATION) - // are not children in the sense of an attached window, we also want to replace - // them at such phases, as they won't be covered by window preservation, - // and in general we expect them to return following relaunch. + // Though windows of TYPE_APPLICATION or TYPE_DRAWN_APPLICATION (as opposed to + // TYPE_BASE_APPLICATION) are not children in the sense of an attached window, + // we also want to replace them at such phases, as they won't be covered by window + // preservation, and in general we expect them to return following relaunch. boolean shouldBeReplacedWithChildren() { - return mIsChildWindow || mAttrs.type == TYPE_APPLICATION; + return mIsChildWindow || mAttrs.type == TYPE_APPLICATION + || mAttrs.type == TYPE_DRAWN_APPLICATION; } public int getRotationAnimationHint() { diff --git a/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java b/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java index daaa4f5c6848..2038c9e35f0c 100644 --- a/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java +++ b/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java @@ -136,6 +136,10 @@ class PreloadAppsInstaller { mApkToPackageMap.put(apkName, basePackageName); } installExistingPackage(basePackageName, userId, counter); + } else { + Log.e(TAG, "Package " + basePackageName + " cannot be installed from " + + apkName + ": " + msg + " (returnCode " + returnCode + ")"); + counter.appInstallFinished(); } } }.getBinder(), 0, SYSTEM_SERVER_PACKAGE_NAME, userId); diff --git a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java index 7525e87c0e12..6b273210263d 100644 --- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java +++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java @@ -89,8 +89,8 @@ public class RetailDemoModeService extends SystemService { private static final long SCREEN_WAKEUP_DELAY = 2500; private static final long USER_INACTIVITY_TIMEOUT_MIN = 10000; - private static final long USER_INACTIVITY_TIMEOUT_DEFAULT = 30000; - private static final long WARNING_DIALOG_TIMEOUT_DEFAULT = 6000; + private static final long USER_INACTIVITY_TIMEOUT_DEFAULT = 90000; + private static final long WARNING_DIALOG_TIMEOUT_DEFAULT = 0; private static final long MILLIS_PER_SECOND = 1000; private static final int[] VOLUME_STREAMS_TO_MUTE = { diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java index 59ccbd93f3fb..be1642e91319 100644 --- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java @@ -1047,7 +1047,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { NETWORK_CAPABILITIES, LINK_PROPERTIES, LOSING, - LOST + LOST, + UNAVAILABLE } /** @@ -1088,6 +1089,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { } @Override + public void onUnavailable() { + setLastCallback(CallbackState.UNAVAILABLE, null, null); + } + + @Override public void onLosing(Network network, int maxMsToLive) { setLastCallback(CallbackState.LOSING, network, maxMsToLive /* autoboxed int */); } @@ -1953,6 +1959,79 @@ public class ConnectivityServiceTest extends AndroidTestCase { handlerThread.quit(); } + /** + * Validate that a satisfied network request does not trigger onUnavailable() once the + * time-out period expires. + */ + @SmallTest + public void testSatisfiedNetworkRequestDoesNotTriggerOnUnavailable() { + NetworkRequest nr = new NetworkRequest.Builder().addTransportType( + NetworkCapabilities.TRANSPORT_WIFI).build(); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + mCm.requestNetwork(nr, networkCallback, 10); + + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + + // pass timeout and validate that UNAVAILABLE is not called + try { + Thread.sleep(15); + } catch (InterruptedException e) { + } + networkCallback.assertNoCallback(); + } + + /** + * Validate that when a time-out is specified for a network request the onUnavailable() + * callback is called when time-out expires. Then validate that if network request is + * (somehow) satisfied - the callback isn't called later. + */ + @SmallTest + public void testTimedoutNetworkRequest() { + NetworkRequest nr = new NetworkRequest.Builder().addTransportType( + NetworkCapabilities.TRANSPORT_WIFI).build(); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + mCm.requestNetwork(nr, networkCallback, 10); + + // pass timeout and validate that UNAVAILABLE is called + networkCallback.expectCallback(CallbackState.UNAVAILABLE, null); + + // create a network satisfying request - validate that request not triggered + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + networkCallback.assertNoCallback(); + } + + /** + * Validate that when a network request is unregistered (cancelled) the time-out for that + * request doesn't trigger the onUnavailable() callback. + */ + @SmallTest + public void testTimedoutAfterUnregisteredNetworkRequest() { + NetworkRequest nr = new NetworkRequest.Builder().addTransportType( + NetworkCapabilities.TRANSPORT_WIFI).build(); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + mCm.requestNetwork(nr, networkCallback, 10); + + // remove request + mCm.unregisterNetworkCallback(networkCallback); + + // pass timeout and validate that no callbacks + // Note: doesn't validate that nothing called from CS since even if called the CM already + // unregisters the callback and won't pass it through! + try { + Thread.sleep(15); + } catch (InterruptedException e) { + } + networkCallback.assertNoCallback(); + + // create a network satisfying request - validate that request not triggered + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + networkCallback.assertNoCallback(); + } + private static class TestKeepaliveCallback extends PacketKeepaliveCallback { public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR }; diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java index 94c6711da9ae..5e3c8bd49f0b 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java @@ -40,7 +40,9 @@ import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; + import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL; + import static org.easymock.EasyMock.anyInt; import static org.easymock.EasyMock.anyLong; import static org.easymock.EasyMock.anyObject; @@ -49,12 +51,12 @@ import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; -import static org.easymock.EasyMock.isA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.app.AlarmManager; -import android.app.IAlarmListener; -import android.app.IAlarmManager; -import android.app.PendingIntent; import android.app.usage.NetworkStatsManager; import android.content.Context; import android.content.Intent; @@ -63,6 +65,7 @@ import android.net.IConnectivityManager; import android.net.INetworkManagementEventObserver; import android.net.INetworkStatsSession; import android.net.LinkProperties; +import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkState; @@ -80,11 +83,10 @@ import android.os.MessageQueue; import android.os.MessageQueue.IdleHandler; import android.os.Message; import android.os.PowerManager; -import android.os.WorkSource; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; import android.telephony.TelephonyManager; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.Suppress; import android.util.TrustedTime; import com.android.internal.net.VpnInfo; @@ -97,6 +99,10 @@ import libcore.io.IoUtils; import org.easymock.Capture; import org.easymock.EasyMock; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import java.io.File; import java.util.ArrayList; @@ -109,8 +115,8 @@ import java.util.List; * TODO: This test is really brittle, largely due to overly-strict use of Easymock. * Rewrite w/ Mockito. */ -@LargeTest -public class NetworkStatsServiceTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class NetworkStatsServiceTest { private static final String TAG = "NetworkStatsServiceTest"; private static final String TEST_IFACE = "test0"; @@ -148,12 +154,12 @@ public class NetworkStatsServiceTest extends AndroidTestCase { private INetworkStatsSession mSession; private INetworkManagementEventObserver mNetworkObserver; - @Override + @Before public void setUp() throws Exception { - super.setUp(); + final Context context = InstrumentationRegistry.getContext(); - mServiceContext = new BroadcastInterceptingContext(getContext()); - mStatsDir = getContext().getFilesDir(); + mServiceContext = new BroadcastInterceptingContext(context); + mStatsDir = context.getFilesDir(); if (mStatsDir.exists()) { IoUtils.deleteContents(mStatsDir); } @@ -205,7 +211,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { } - @Override + @After public void tearDown() throws Exception { IoUtils.deleteContents(mStatsDir); @@ -219,10 +225,9 @@ public class NetworkStatsServiceTest extends AndroidTestCase { mSession.close(); mService = null; - - super.tearDown(); } + @Test public void testNetworkStatsWifi() throws Exception { // pretend that wifi network comes online; service should ask about full // network state, and poll any existing interfaces before updating. @@ -276,6 +281,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { } + @Test public void testStatsRebootPersist() throws Exception { assertStatsFilesExist(false); @@ -366,7 +372,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { } // TODO: simulate reboot to test bucket resize - @Suppress + // @Test public void testStatsBucketResize() throws Exception { NetworkStatsHistory history = null; @@ -425,6 +431,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { } + @Test public void testUidStatsAcrossNetworks() throws Exception { // pretend first mobile network comes online expectCurrentTime(); @@ -515,6 +522,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { } + @Test public void testUidRemovedIsMoved() throws Exception { // pretend that network comes online expectCurrentTime(); @@ -585,6 +593,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { } + @Test public void testUid3g4gCombinedByTemplate() throws Exception { // pretend that network comes online expectCurrentTime(); @@ -658,6 +667,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { verifyAndReset(); } + @Test public void testSummaryForAllUid() throws Exception { // pretend that network comes online expectCurrentTime(); @@ -729,6 +739,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { verifyAndReset(); } + @Test public void testForegroundBackground() throws Exception { // pretend that network comes online expectCurrentTime(); @@ -799,6 +810,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { verifyAndReset(); } + @Test public void testRoaming() throws Exception { // pretend that network comes online expectCurrentTime(); @@ -846,6 +858,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { verifyAndReset(); } + @Test public void testTethering() throws Exception { // pretend first mobile network comes online expectCurrentTime(); @@ -887,6 +900,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { } + @Test public void testRegisterUsageCallback() throws Exception { // pretend that wifi network comes online; service should ask about full // network state, and poll any existing interfaces before updating. @@ -1005,6 +1019,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { EasyMock.verify(mockBinder); } + @Test public void testUnregisterUsageCallback_unknown_noop() throws Exception { String callingPackage = "the.calling.package"; long thresholdInBytes = 10 * 1024 * 1024; // 10 MB @@ -1204,7 +1219,8 @@ public class NetworkStatsServiceTest extends AndroidTestCase { info.setDetailedState(DetailedState.CONNECTED, null, null); final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(TEST_IFACE); - return new NetworkState(info, prop, null, null, null, TEST_SSID); + final NetworkCapabilities capabilities = new NetworkCapabilities(); + return new NetworkState(info, prop, capabilities, null, null, TEST_SSID); } private static NetworkState buildMobile3gState(String subscriberId) { @@ -1218,7 +1234,8 @@ public class NetworkStatsServiceTest extends AndroidTestCase { info.setRoaming(isRoaming); final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(TEST_IFACE); - return new NetworkState(info, prop, null, null, subscriberId, null); + final NetworkCapabilities capabilities = new NetworkCapabilities(); + return new NetworkState(info, prop, capabilities, null, subscriberId, null); } private static NetworkState buildMobile4gState(String iface) { @@ -1226,7 +1243,8 @@ public class NetworkStatsServiceTest extends AndroidTestCase { info.setDetailedState(DetailedState.CONNECTED, null, null); final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(iface); - return new NetworkState(info, prop, null, null, null, null); + final NetworkCapabilities capabilities = new NetworkCapabilities(); + return new NetworkState(info, prop, capabilities, null, null, null); } private NetworkStats buildEmptyStats() { diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java index ba83be19bed9..64c562278227 100644 --- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java @@ -39,7 +39,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase { public PackageSetting generateFakePackageSetting(String name) { return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"), new File(mContext.getCacheDir(), "fakeResPath"), "", "", "", - "", 1, 0, 0, null, null); + "", 1, 0, 0, null, null, 0 /*sharedUserId*/); } @Override diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index a9391344c7b4..09bd12c17a74 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -522,7 +522,8 @@ public class PackageManagerSettingsTests { pkgFlags, 0 /*privateFlags*/, null /*parentPackageName*/, - null /*childPackageNames*/); + null /*childPackageNames*/, + 0 /*sharedUserId*/); } private @NonNull List<UserInfo> createFakeUsers() { diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java index 9ccb1a618568..0bd014c874b5 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java @@ -67,7 +67,7 @@ public class AppIdleHistoryTests extends AndroidTestCase { // Screen on time should not keep progressing with screen is off assertEquals(aih.getScreenOnTimeLocked(7000), 3000); assertEquals(aih.getScreenOnTimeLocked(8000), 3000); - aih.writeElapsedTimeLocked(); + aih.writeAppIdleDurationsLocked(); // Check if the screen on time is persisted across instantiations AppIdleHistory aih2 = new AppIdleHistory(mStorageDir, 0); diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk index 701e05857142..2818457c9ac9 100644 --- a/services/tests/shortcutmanagerutils/Android.mk +++ b/services/tests/shortcutmanagerutils/Android.mk @@ -26,6 +26,6 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE := ShortcutManagerTestUtils -LOCAL_SDK_VERSION := current +LOCAL_SDK_VERSION := test_current include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java index 78f95c4d217e..efd7b97b5406 100644 --- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java +++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java @@ -156,10 +156,11 @@ public class ShortcutManagerTestUtils { return result; } - private static List<String> runCommand(Instrumentation instrumentation, String command) { + public static List<String> runCommand(Instrumentation instrumentation, String command) { return runCommand(instrumentation, command, null); } - private static List<String> runCommand(Instrumentation instrumentation, String command, + + public static List<String> runCommand(Instrumentation instrumentation, String command, Predicate<List<String>> resultAsserter) { Log.d(TAG, "Running command: " + command); final List<String> result; @@ -175,11 +176,11 @@ public class ShortcutManagerTestUtils { return result; } - private static void runCommandForNoOutput(Instrumentation instrumentation, String command) { + public static void runCommandForNoOutput(Instrumentation instrumentation, String command) { runCommand(instrumentation, command, result -> result.size() == 0); } - private static List<String> runShortcutCommand(Instrumentation instrumentation, String command, + public static List<String> runShortcutCommand(Instrumentation instrumentation, String command, Predicate<List<String>> resultAsserter) { return runCommand(instrumentation, "cmd shortcut " + command, resultAsserter); } @@ -204,7 +205,8 @@ public class ShortcutManagerTestUtils { } public static void setDefaultLauncher(Instrumentation instrumentation, String component) { - runCommand(instrumentation, "cmd package set-home-activity " + component, + runCommand(instrumentation, "cmd package set-home-activity --user " + + instrumentation.getContext().getUserId() + " " + component, result -> result.contains("Success")); } diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java index a3313c9741cd..f69dae44a316 100644 --- a/services/usage/java/com/android/server/usage/AppIdleHistory.java +++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java @@ -118,7 +118,6 @@ public class AppIdleHistory { } else { mScreenOnDuration += elapsedRealtime - mScreenOnSnapshot; mElapsedDuration += elapsedRealtime - mElapsedSnapshot; - writeScreenOnTimeLocked(); mElapsedSnapshot = elapsedRealtime; } } @@ -167,7 +166,7 @@ public class AppIdleHistory { /** * To be called periodically to keep track of elapsed time when app idle times are written */ - public void writeElapsedTimeLocked() { + public void writeAppIdleDurationsLocked() { final long elapsedRealtime = SystemClock.elapsedRealtime(); // Only bump up and snapshot the elapsed time. Don't change screen on duration. mElapsedDuration += elapsedRealtime - mElapsedSnapshot; diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 69cf1a2c5362..828477302245 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -304,9 +304,9 @@ public class UsageStatsService extends SystemService implements @Override public void onDisplayChanged(int displayId) { if (displayId == Display.DEFAULT_DISPLAY) { + final boolean displayOn = isDisplayOn(); synchronized (UsageStatsService.this.mLock) { - mAppIdleHistory.updateDisplayLocked(isDisplayOn(), - SystemClock.elapsedRealtime()); + mAppIdleHistory.updateDisplayLocked(displayOn, SystemClock.elapsedRealtime()); } } } @@ -1005,9 +1005,9 @@ public class UsageStatsService extends SystemService implements service.persistActiveStats(); mAppIdleHistory.writeAppIdleTimesLocked(mUserState.keyAt(i)); } - // Persist elapsed time periodically, in case screen doesn't get toggled - // until the next boot - mAppIdleHistory.writeElapsedTimeLocked(); + // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be + // considered not-idle, which is the safest outcome in such an event. + mAppIdleHistory.writeAppIdleDurationsLocked(); mHandler.removeMessages(MSG_FLUSH_TO_DISK); } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 4da5ff28204e..a093d54568d3 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -461,6 +461,32 @@ public abstract class Connection extends Conferenceable { */ public static final String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED"; + /** + * Connection event used to inform {@link InCallService}s when a call has been put on hold by + * the remote party. + * <p> + * This is different than the {@link Connection#STATE_HOLDING} state which indicates that the + * call is being held locally on the device. When a capable {@link ConnectionService} receives + * signalling to indicate that the remote party has put the call on hold, it can send this + * connection event. + * @hide + */ + public static final String EVENT_CALL_REMOTELY_HELD = + "android.telecom.event.CALL_REMOTELY_HELD"; + + /** + * Connection event used to inform {@link InCallService}s when a call which was remotely held + * (see {@link #EVENT_CALL_REMOTELY_HELD}) has been un-held by the remote party. + * <p> + * This is different than the {@link Connection#STATE_HOLDING} state which indicates that the + * call is being held locally on the device. When a capable {@link ConnectionService} receives + * signalling to indicate that the remote party has taken the call off hold, it can send this + * connection event. + * @hide + */ + public static final String EVENT_CALL_REMOTELY_UNHELD = + "android.telecom.event.CALL_REMOTELY_UNHELD"; + // Flag controlling whether PII is emitted into the logs private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG); diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java index 306b3c15fa11..c4739ff13a19 100644 --- a/telecomm/java/android/telecom/RemoteConnectionService.java +++ b/telecomm/java/android/telecom/RemoteConnectionService.java @@ -224,6 +224,7 @@ final class RemoteConnectionService { conference.setState(parcel.getState()); conference.setConnectionCapabilities(parcel.getConnectionCapabilities()); + conference.setConnectionProperties(parcel.getConnectionProperties()); mConferenceById.put(callId, conference); conference.registerCallback(new RemoteConference.Callback() { @Override diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index e666986aae50..fe9a93c74c8c 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -246,6 +246,29 @@ public class CarrierConfigManager { public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool"; /** + * Flag specifying whether the carrier wants to notify the user when a VT call has been handed + * over from WIFI to LTE. + * <p> + * The handover notification is sent as a + * {@link TelephonyManager#EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE} + * {@link android.telecom.Connection} event, which an {@link android.telecom.InCallService} + * should use to trigger the display of a user-facing message. + * <p> + * The Connection event is sent to the InCallService only once, the first time it occurs. + * @hide + */ + public static final String KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL = + "notify_handover_video_from_wifi_to_lte_bool"; + + /** + * Flag specifying whether the carrier supports downgrading a video call (tx, rx or tx/rx) + * directly to an audio call. + * @hide + */ + public static final String KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL = + "support_downgrade_vt_to_audio_bool"; + + /** * Flag specifying whether WFC over IMS should be available for carrier: independent of * carrier provisioning. If false: hard disabled. If true: then depends on carrier * provisioning, availability etc. @@ -919,6 +942,15 @@ public class CarrierConfigManager { public static final String KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL = "notify_vt_handover_to_wifi_failure_bool"; + /** + * A upper case list of CNAP names that are unhelpful to the user for distinguising calls and + * should be filtered out of the CNAP information. This includes CNAP names such as "WIRELESS + * CALLER" or "UNKNOWN NAME". By default, if there are no filtered names for this carrier, null + * is returned. + * @hide + */ + public static final String FILTERED_CNAP_NAMES_STRING_ARRAY = "filtered_cnap_names_string_array"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -933,6 +965,8 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false); + sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false); + sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true); sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false); @@ -1085,6 +1119,7 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null); sDefaults.putBoolean(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL, false); sDefaults.putBoolean(KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, false); + sDefaults.putStringArray(FILTERED_CNAP_NAMES_STRING_ARRAY, null); } /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 4138aa09f99a..68a390dbab20 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -705,6 +705,17 @@ public class TelephonyManager { "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT"; /** + * {@link android.telecom.Connection} event used to indicate that an IMS call has be + * successfully handed over from WIFI to LTE. + * <p> + * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}. + * The {@link Bundle} parameter is expected to be null when this connection event is used. + * @hide + */ + public static final String EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE = + "android.telephony.event.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE"; + + /** * {@link android.telecom.Connection} event used to indicate that an IMS call failed to be * handed over from LTE to WIFI. * <p> @@ -716,6 +727,28 @@ public class TelephonyManager { "android.telephony.event.EVENT_HANDOVER_TO_WIFI_FAILED"; /** + * {@link android.telecom.Connection} event used to indicate that a video call was downgraded to + * audio because the data limit was reached. + * <p> + * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}. + * The {@link Bundle} parameter is expected to be null when this connection event is used. + * @hide + */ + public static final String EVENT_DOWNGRADE_DATA_LIMIT_REACHED = + "android.telephony.event.EVENT_DOWNGRADE_DATA_LIMIT_REACHED"; + + /** + * {@link android.telecom.Connection} event used to indicate that a video call was downgraded to + * audio because the data was disabled. + * <p> + * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}. + * The {@link Bundle} parameter is expected to be null when this connection event is used. + * @hide + */ + public static final String EVENT_DOWNGRADE_DATA_DISABLED = + "android.telephony.event.EVENT_DOWNGRADE_DATA_DISABLED"; + + /** * Response codes for sim activation. Activation completed successfully. * @hide */ diff --git a/tests/TouchLatency/.gitignore b/tests/TouchLatency/.gitignore new file mode 100644 index 000000000000..cfb71643044b --- /dev/null +++ b/tests/TouchLatency/.gitignore @@ -0,0 +1,5 @@ +.gradle +/local.properties +/.idea +.DS_Store +/build diff --git a/tests/TouchLatency/Android.mk b/tests/TouchLatency/Android.mk new file mode 100644 index 000000000000..73b5b6c2e7b8 --- /dev/null +++ b/tests/TouchLatency/Android.mk @@ -0,0 +1,27 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_MANIFEST_FILE := app/src/main/AndroidManifest.xml + +# omit gradle 'build' dir +LOCAL_SRC_FILES := $(call all-java-files-under,app/src/main/java) + +# use appcompat/support lib from the tree, so improvements/ +# regressions are reflected in test data +LOCAL_RESOURCE_DIR := \ + $(LOCAL_PATH)/app/src/main/res \ + frameworks/support/v7/appcompat/res + +LOCAL_AAPT_FLAGS := \ + --auto-add-overlay \ + --extra-packages android.support.v7.appcompat + +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-v4 \ + android-support-v7-appcompat + +LOCAL_PACKAGE_NAME := TouchLatency + +include $(BUILD_PACKAGE) diff --git a/tests/TouchLatency/TouchLatency.iml b/tests/TouchLatency/TouchLatency.iml new file mode 100644 index 000000000000..cd87cea19f1d --- /dev/null +++ b/tests/TouchLatency/TouchLatency.iml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4"> + <component name="FacetManager"> + <facet type="java-gradle" name="Java-Gradle"> + <configuration> + <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" /> + <option name="BUILDABLE" value="false" /> + </configuration> + </facet> + </component> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <excludeFolder url="file://$MODULE_DIR$/.gradle" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module>
\ No newline at end of file diff --git a/tests/TouchLatency/app/.gitignore b/tests/TouchLatency/app/.gitignore new file mode 100644 index 000000000000..796b96d1c402 --- /dev/null +++ b/tests/TouchLatency/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/tests/TouchLatency/app/app.iml b/tests/TouchLatency/app/app.iml new file mode 100644 index 000000000000..689e5e0024da --- /dev/null +++ b/tests/TouchLatency/app/app.iml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="TouchLatency" external.system.module.version="unspecified" type="JAVA_MODULE" version="4"> + <component name="FacetManager"> + <facet type="android-gradle" name="Android-Gradle"> + <configuration> + <option name="GRADLE_PROJECT_PATH" value=":app" /> + </configuration> + </facet> + <facet type="android" name="Android"> + <configuration> + <option name="SELECTED_BUILD_VARIANT" value="debug" /> + <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" /> + <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" /> + <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" /> + <option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" /> + <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" /> + <option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugAndroidTestSources" /> + <option name="ALLOW_USER_CONFIGURATION" value="false" /> + <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" /> + <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" /> + <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" /> + <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" /> + </configuration> + </facet> + </component> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" /> + <output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" /> + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" /> + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" /> + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" /> + <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" /> + <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" /> + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" /> + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" /> + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" /> + <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" /> + <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" /> + <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" /> + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" /> + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" /> + <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" /> + <excludeFolder url="file://$MODULE_DIR$/build/outputs" /> + <excludeFolder url="file://$MODULE_DIR$/build/tmp" /> + </content> + <orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" exported="" name="appcompat-v7-21.0.3" level="project" /> + <orderEntry type="library" exported="" name="support-annotations-21.0.3" level="project" /> + <orderEntry type="library" exported="" name="support-v4-21.0.3" level="project" /> + </component> +</module>
\ No newline at end of file diff --git a/tests/TouchLatency/app/build.gradle b/tests/TouchLatency/app/build.gradle new file mode 100644 index 000000000000..7133beb8efee --- /dev/null +++ b/tests/TouchLatency/app/build.gradle @@ -0,0 +1,25 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + defaultConfig { + applicationId "com.prefabulated.touchlatency" + minSdkVersion 21 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:21.0.3' +} diff --git a/tests/TouchLatency/app/proguard-rules.pro b/tests/TouchLatency/app/proguard-rules.pro new file mode 100644 index 000000000000..de32a749cc70 --- /dev/null +++ b/tests/TouchLatency/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /usr/local/google/home/stoza/android-sdk-linux/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/tests/TouchLatency/app/src/androidTest/java/com/prefabulated/touchlatency/ApplicationTest.java b/tests/TouchLatency/app/src/androidTest/java/com/prefabulated/touchlatency/ApplicationTest.java new file mode 100644 index 000000000000..717e3974919d --- /dev/null +++ b/tests/TouchLatency/app/src/androidTest/java/com/prefabulated/touchlatency/ApplicationTest.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 com.prefabulated.touchlatency; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a> + */ +public class ApplicationTest extends ApplicationTestCase<Application> { + public ApplicationTest() { + super(Application.class); + } +} diff --git a/tests/TouchLatency/app/src/main/AndroidManifest.xml b/tests/TouchLatency/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..e4aa4dc6c3f0 --- /dev/null +++ b/tests/TouchLatency/app/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.prefabulated.touchlatency" > + + <application + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name=".TouchLatencyActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java new file mode 100644 index 000000000000..7c139742f54d --- /dev/null +++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java @@ -0,0 +1,210 @@ +/* + * 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.prefabulated.touchlatency; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.os.CountDownTimer; +import android.support.v7.app.ActionBarActivity; +import android.os.Bundle; +import android.text.method.Touch; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; + +import java.util.ArrayList; +import java.util.Collections; + +class TouchLatencyView extends View implements View.OnTouchListener { + private static final String LOG_TAG = "TouchLatency"; + private static final int BACKGROUND_COLOR = 0xFF400080; + private static final int INNER_RADIUS = 70; + private static final int BALL_RADIUS = 100; + + public TouchLatencyView(Context context, AttributeSet attrs) { + super(context, attrs); + setOnTouchListener(this); + setWillNotDraw(false); + mBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mBluePaint.setColor(0xFF0000FF); + mBluePaint.setStyle(Paint.Style.FILL); + mGreenPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mGreenPaint.setColor(0xFF00FF00); + mGreenPaint.setStyle(Paint.Style.FILL); + mYellowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mYellowPaint.setColor(0xFFFFFF00); + mYellowPaint.setStyle(Paint.Style.FILL); + mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mRedPaint.setColor(0xFFFF0000); + mRedPaint.setStyle(Paint.Style.FILL); + + mTouching = false; + + mBallX = 100.0f; + mBallY = 100.0f; + mVelocityX = 7.0f; + mVelocityY = 7.0f; + } + + @Override + public boolean onTouch(View view, MotionEvent event) { + int action = event.getActionMasked(); + if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) { + mTouching = true; + invalidate(); + } else if (action == MotionEvent.ACTION_UP) { + mTouching = false; + invalidate(); + return true; + } else { + return true; + } + mTouchX = event.getX(); + mTouchY = event.getY(); + return true; + } + + private void drawTouch(Canvas canvas) { + if (!mTouching) { + Log.d(LOG_TAG, "Filling background"); + canvas.drawColor(BACKGROUND_COLOR); + return; + } + + float deltaX = (mTouchX - mLastDrawnX); + float deltaY = (mTouchY - mLastDrawnY); + float scaleFactor = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 1.5f; + + mLastDrawnX = mTouchX; + mLastDrawnY = mTouchY; + + canvas.drawColor(BACKGROUND_COLOR); + canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 3 * scaleFactor, mRedPaint); + canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 2 * scaleFactor, mYellowPaint); + canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + scaleFactor, mGreenPaint); + canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS, mBluePaint); + } + + private void drawBall(Canvas canvas) { + int width = canvas.getWidth(); + int height = canvas.getHeight(); + + // Update position + mBallX += mVelocityX; + mBallY += mVelocityY; + + // Clamp and change velocity if necessary + float left = mBallX - BALL_RADIUS; + if (left < 0) { + left = 0; + mVelocityX *= -1; + } + + float top = mBallY - BALL_RADIUS; + if (top < 0) { + top = 0; + mVelocityY *= -1; + } + + float right = mBallX + BALL_RADIUS; + if (right > width) { + right = width; + mVelocityX *= -1; + } + + float bottom = mBallY + BALL_RADIUS; + if (bottom > height) { + bottom = height; + mVelocityY *= -1; + } + + // Draw the ball + canvas.drawColor(BACKGROUND_COLOR); + canvas.drawOval(left, top, right, bottom, mYellowPaint); + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (mMode == 0) { + drawTouch(canvas); + } else { + drawBall(canvas); + } + } + + public void changeMode(MenuItem item) { + final int NUM_MODES = 2; + final String modes[] = {"Touch", "Ball"}; + mMode = (mMode + 1) % NUM_MODES; + invalidate(); + item.setTitle(modes[mMode]); + } + + private Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint; + private int mMode; + + private boolean mTouching; + private float mTouchX, mTouchY; + private float mLastDrawnX, mLastDrawnY; + + private float mBallX, mBallY; + private float mVelocityX, mVelocityY; +} + +public class TouchLatencyActivity extends ActionBarActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_touch_latency); + + mTouchView = (TouchLatencyView) findViewById(R.id.canvasView); + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_touch_latency, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + mTouchView.changeMode(item); + } + + return super.onOptionsItemSelected(item); + } + + private TouchLatencyView mTouchView; +} diff --git a/tests/TouchLatency/app/src/main/res/layout/activity_touch_latency.xml b/tests/TouchLatency/app/src/main/res/layout/activity_touch_latency.xml new file mode 100644 index 000000000000..8d20ff24bfe5 --- /dev/null +++ b/tests/TouchLatency/app/src/main/res/layout/activity_touch_latency.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<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" android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".TouchLatencyActivity"> + + <com.prefabulated.touchlatency.TouchLatencyView + android:id = "@+id/canvasView" + android:layout_width="fill_parent" + android:layout_height="fill_parent" /> + +</RelativeLayout> diff --git a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml new file mode 100644 index 000000000000..1824f4a68995 --- /dev/null +++ b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" tools:context=".TouchLatencyActivity"> + <item android:id="@+id/action_settings" android:title="@string/mode" + android:orderInCategory="100" app:showAsAction="always" /> +</menu> diff --git a/tests/TouchLatency/app/src/main/res/mipmap-hdpi/ic_launcher.png b/tests/TouchLatency/app/src/main/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..cde69bcccec6 --- /dev/null +++ b/tests/TouchLatency/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/tests/TouchLatency/app/src/main/res/mipmap-mdpi/ic_launcher.png b/tests/TouchLatency/app/src/main/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..c133a0cbd379 --- /dev/null +++ b/tests/TouchLatency/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/tests/TouchLatency/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/tests/TouchLatency/app/src/main/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..bfa42f0e7b91 --- /dev/null +++ b/tests/TouchLatency/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/tests/TouchLatency/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/tests/TouchLatency/app/src/main/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..324e72cdd748 --- /dev/null +++ b/tests/TouchLatency/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/tests/TouchLatency/app/src/main/res/values-w820dp/dimens.xml b/tests/TouchLatency/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 000000000000..1f222e1eb0bc --- /dev/null +++ b/tests/TouchLatency/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- Example customization of dimensions originally defined in res/values/dimens.xml + (such as screen margins) for screens with more than 820dp of available width. This + would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> + <dimen name="activity_horizontal_margin">64dp</dimen> +</resources> diff --git a/tests/TouchLatency/app/src/main/res/values/dimens.xml b/tests/TouchLatency/app/src/main/res/values/dimens.xml new file mode 100644 index 000000000000..5eeebd7dec7e --- /dev/null +++ b/tests/TouchLatency/app/src/main/res/values/dimens.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">0dp</dimen> + <dimen name="activity_vertical_margin">0dp</dimen> +</resources> diff --git a/tests/TouchLatency/app/src/main/res/values/strings.xml b/tests/TouchLatency/app/src/main/res/values/strings.xml new file mode 100644 index 000000000000..b97f095d501e --- /dev/null +++ b/tests/TouchLatency/app/src/main/res/values/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <string name="app_name">Touch Latency</string> + + <string name="mode">Touch</string> +</resources> diff --git a/tests/TouchLatency/app/src/main/res/values/styles.xml b/tests/TouchLatency/app/src/main/res/values/styles.xml new file mode 100644 index 000000000000..aa7c09fff40d --- /dev/null +++ b/tests/TouchLatency/app/src/main/res/values/styles.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + + <!-- Base application theme. --> + <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> + <!-- Customize your theme here. --> + </style> + +</resources> diff --git a/tests/TouchLatency/build.gradle b/tests/TouchLatency/build.gradle new file mode 100644 index 000000000000..d3ff69d6e7f9 --- /dev/null +++ b/tests/TouchLatency/build.gradle @@ -0,0 +1,19 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.1.0' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/tests/TouchLatency/gradle.properties b/tests/TouchLatency/gradle.properties new file mode 100644 index 000000000000..1d3591c8a4c9 --- /dev/null +++ b/tests/TouchLatency/gradle.properties @@ -0,0 +1,18 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true
\ No newline at end of file diff --git a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.jar b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 000000000000..8c0fb64a8698 --- /dev/null +++ b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.jar diff --git a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..0c71e760dc93 --- /dev/null +++ b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/tests/TouchLatency/gradlew b/tests/TouchLatency/gradlew new file mode 100755 index 000000000000..91a7e269e19d --- /dev/null +++ b/tests/TouchLatency/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/tests/TouchLatency/gradlew.bat b/tests/TouchLatency/gradlew.bat new file mode 100644 index 000000000000..aec99730b4e8 --- /dev/null +++ b/tests/TouchLatency/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/tests/TouchLatency/settings.gradle b/tests/TouchLatency/settings.gradle new file mode 100644 index 000000000000..e7b4def49cb5 --- /dev/null +++ b/tests/TouchLatency/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index b4c4d05a7397..1e7875d435eb 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -1033,7 +1033,6 @@ static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) return NO_ERROR; } - ResXMLTree tree; Asset* asset = assets.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_STREAMING); if (asset == NULL) { fprintf(stderr, "ERROR: Platform AndroidManifest.xml not found\n"); @@ -1041,11 +1040,17 @@ static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) } ssize_t result = NO_ERROR; - if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) { - fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n"); - result = UNKNOWN_ERROR; - } else { - result = extractPlatformBuildVersion(tree, bundle); + + // Create a new scope so that ResXMLTree is destroyed before we delete the memory over + // which it iterates (asset). + { + ResXMLTree tree; + if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) { + fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n"); + result = UNKNOWN_ERROR; + } else { + result = extractPlatformBuildVersion(tree, bundle); + } } delete asset; diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 76c59dd57068..661409e3d4bc 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -4860,24 +4860,39 @@ void ResourceTable::getDensityVaryingResources( const Vector<sp<Type> >& types = mOrderedPackages[p]->getOrderedTypes(); const size_t typeCount = types.size(); for (size_t t = 0; t < typeCount; t++) { - const Vector<sp<ConfigList> >& configs = types[t]->getOrderedConfigs(); + const sp<Type>& type = types[t]; + if (type == NULL) { + continue; + } + + const Vector<sp<ConfigList> >& configs = type->getOrderedConfigs(); const size_t configCount = configs.size(); for (size_t c = 0; c < configCount; c++) { + const sp<ConfigList>& configList = configs[c]; + if (configList == NULL) { + continue; + } + const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configEntries - = configs[c]->getEntries(); + = configList->getEntries(); const size_t configEntryCount = configEntries.size(); for (size_t ce = 0; ce < configEntryCount; ce++) { + const sp<Entry>& entry = configEntries.valueAt(ce); + if (entry == NULL) { + continue; + } + const ConfigDescription& config = configEntries.keyAt(ce); if (AaptConfig::isDensityOnly(config)) { // This configuration only varies with regards to density. const Symbol symbol( mOrderedPackages[p]->getName(), - types[t]->getName(), - configs[c]->getName(), + type->getName(), + configList->getName(), getResId(mOrderedPackages[p], types[t], - configs[c]->getEntryIndex())); + configList->getEntryIndex())); + - const sp<Entry>& entry = configEntries.valueAt(ce); AaptUtil::appendValue(resources, symbol, SymbolDefinition(symbol, config, entry->getPos())); } diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk index 4f38e9435141..b52c530d03cb 100644 --- a/tools/aapt2/Android.mk +++ b/tools/aapt2/Android.mk @@ -40,6 +40,7 @@ sources := \ link/ReferenceLinker.cpp \ link/TableMerger.cpp \ link/VersionCollapser.cpp \ + link/XmlNamespaceRemover.cpp \ link/XmlReferenceLinker.cpp \ process/SymbolTable.cpp \ proto/ProtoHelpers.cpp \ @@ -89,6 +90,7 @@ testSources := \ link/ReferenceLinker_test.cpp \ link/TableMerger_test.cpp \ link/VersionCollapser_test.cpp \ + link/XmlNamespaceRemover_test.cpp \ link/XmlReferenceLinker_test.cpp \ process/SymbolTable_test.cpp \ proto/TableProtoSerializer_test.cpp \ diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h index 1d39b722b053..a9794a419ca2 100644 --- a/tools/aapt2/AppInfo.h +++ b/tools/aapt2/AppInfo.h @@ -37,6 +37,16 @@ struct AppInfo { * The App's minimum SDK version. */ Maybe<std::string> minSdkVersion; + + /** + * The Version code of the app. + */ + Maybe<uint32_t> versionCode; + + /** + * The revision code of the app. + */ + Maybe<uint32_t> revisionCode; }; } // namespace aapt diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h index e86f2a8830e8..725027c2c45a 100644 --- a/tools/aapt2/Diagnostics.h +++ b/tools/aapt2/Diagnostics.h @@ -41,13 +41,13 @@ private: public: DiagMessage() = default; - DiagMessage(const StringPiece& src) : mSource(src) { + explicit DiagMessage(const StringPiece& src) : mSource(src) { } - DiagMessage(const Source& src) : mSource(src) { + explicit DiagMessage(const Source& src) : mSource(src) { } - DiagMessage(size_t line) : mSource(Source().withLine(line)) { + explicit DiagMessage(size_t line) : mSource(Source().withLine(line)) { } template <typename T> diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index a74b5aa2478e..ed55f852c24c 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -25,7 +25,7 @@ namespace aapt { static const char* sMajorVersion = "2"; // Update minor version whenever a feature or flag is added. -static const char* sMinorVersion = "0"; +static const char* sMinorVersion = "1"; int printVersion() { std::cerr << "Android Asset Packaging Tool (aapt) " diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h index 505a982e5eab..b6aaa4df3bec 100644 --- a/tools/aapt2/NameMangler.h +++ b/tools/aapt2/NameMangler.h @@ -43,7 +43,7 @@ private: NameManglerPolicy mPolicy; public: - NameMangler(NameManglerPolicy policy) : mPolicy(policy) { + explicit NameMangler(NameManglerPolicy policy) : mPolicy(policy) { } Maybe<ResourceName> mangleName(const ResourceName& name) { diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index a144c6ab2be7..bcdf401077ee 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -500,8 +500,8 @@ std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const }; // Process the raw value. - std::unique_ptr<Item> processedItem = ResourceUtils::parseItemForAttribute(rawValue, typeMask, - onCreateReference); + std::unique_ptr<Item> processedItem = ResourceUtils::tryParseItemForAttribute( + rawValue, typeMask, onCreateReference); if (processedItem) { // Fix up the reference. if (Reference* ref = valueCast<Reference>(processedItem.get())) { @@ -528,20 +528,24 @@ std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const bool ResourceParser::parseString(xml::XmlPullParser* parser, ParsedResource* outResource) { bool formatted = true; if (Maybe<StringPiece> formattedAttr = xml::findAttribute(parser, "formatted")) { - if (!ResourceUtils::tryParseBool(formattedAttr.value(), &formatted)) { + Maybe<bool> maybeFormatted = ResourceUtils::parseBool(formattedAttr.value()); + if (!maybeFormatted) { mDiag->error(DiagMessage(outResource->source) << "invalid value for 'formatted'. Must be a boolean"); return false; } + formatted = maybeFormatted.value(); } bool translateable = mOptions.translatable; if (Maybe<StringPiece> translateableAttr = xml::findAttribute(parser, "translatable")) { - if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) { + Maybe<bool> maybeTranslateable = ResourceUtils::parseBool(translateableAttr.value()); + if (!maybeTranslateable) { mDiag->error(DiagMessage(outResource->source) << "invalid value for 'translatable'. Must be a boolean"); return false; } + translateable = maybeTranslateable.value(); } outResource->value = parseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString); @@ -590,7 +594,7 @@ bool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* out outResource->name.type = *parsedType; if (Maybe<StringPiece> maybeIdStr = xml::findNonEmptyAttribute(parser, "id")) { - Maybe<ResourceId> maybeId = ResourceUtils::tryParseResourceId(maybeIdStr.value()); + Maybe<ResourceId> maybeId = ResourceUtils::parseResourceId(maybeIdStr.value()); if (!maybeId) { mDiag->error(DiagMessage(outResource->source) << "invalid resource ID '" << maybeId.value() << "' in <public>"); @@ -630,7 +634,7 @@ bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource return false; } - Maybe<ResourceId> maybeId = ResourceUtils::tryParseResourceId(maybeIdStr.value()); + Maybe<ResourceId> maybeId = ResourceUtils::parseResourceId(maybeIdStr.value()); if (!maybeId) { mDiag->error(DiagMessage(outResource->source) << "invalid resource ID '" << maybeIdStr.value() << "' in <public-group>"); @@ -1058,14 +1062,17 @@ bool ResourceParser::parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* bool translateable = mOptions.translatable; if (Maybe<StringPiece> translateableAttr = xml::findAttribute(parser, "translatable")) { - if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) { + Maybe<bool> maybeTranslateable = ResourceUtils::parseBool(translateableAttr.value()); + if (!maybeTranslateable) { mDiag->error(DiagMessage(outResource->source) << "invalid value for 'translatable'. Must be a boolean"); return false; } + translateable = maybeTranslateable.value(); } array->setTranslateable(translateable); + bool error = false; const size_t depth = parser->getDepth(); while (xml::XmlPullParser::nextChildNode(parser, depth)) { diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 773616d6aa71..11619fa9c67d 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -124,7 +124,7 @@ bool parseResourceName(const StringPiece& str, ResourceNameRef* outRef, bool* ou return true; } -bool tryParseReference(const StringPiece& str, ResourceNameRef* outRef, bool* outCreate, +bool parseReference(const StringPiece& str, ResourceNameRef* outRef, bool* outCreate, bool* outPrivate) { StringPiece trimmedStr(util::trimWhitespace(str)); if (trimmedStr.empty()) { @@ -171,10 +171,10 @@ bool tryParseReference(const StringPiece& str, ResourceNameRef* outRef, bool* ou } bool isReference(const StringPiece& str) { - return tryParseReference(str, nullptr, nullptr, nullptr); + return parseReference(str, nullptr, nullptr, nullptr); } -bool tryParseAttributeReference(const StringPiece& str, ResourceNameRef* outRef) { +bool parseAttributeReference(const StringPiece& str, ResourceNameRef* outRef) { StringPiece trimmedStr(util::trimWhitespace(str)); if (trimmedStr.empty()) { return false; @@ -208,7 +208,7 @@ bool tryParseAttributeReference(const StringPiece& str, ResourceNameRef* outRef) } bool isAttributeReference(const StringPiece& str) { - return tryParseAttributeReference(str, nullptr); + return parseAttributeReference(str, nullptr); } /* @@ -271,13 +271,13 @@ Maybe<Reference> parseStyleParentReference(const StringPiece& str, std::string* std::unique_ptr<Reference> tryParseReference(const StringPiece& str, bool* outCreate) { ResourceNameRef ref; bool privateRef = false; - if (tryParseReference(str, &ref, outCreate, &privateRef)) { + if (parseReference(str, &ref, outCreate, &privateRef)) { std::unique_ptr<Reference> value = util::make_unique<Reference>(ref); value->privateReference = privateRef; return value; } - if (tryParseAttributeReference(str, &ref)) { + if (parseAttributeReference(str, &ref)) { if (outCreate) { *outCreate = false; } @@ -420,23 +420,26 @@ std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str) { return error ? std::unique_ptr<BinaryPrimitive>() : util::make_unique<BinaryPrimitive>(value); } -bool tryParseBool(const StringPiece& str, bool* outValue) { +Maybe<bool> parseBool(const StringPiece& str) { StringPiece trimmedStr(util::trimWhitespace(str)); if (trimmedStr == "true" || trimmedStr == "TRUE" || trimmedStr == "True") { - if (outValue) { - *outValue = true; - } - return true; + return Maybe<bool>(true); } else if (trimmedStr == "false" || trimmedStr == "FALSE" || trimmedStr == "False") { - if (outValue) { - *outValue = false; - } - return true; + return Maybe<bool>(false); } - return false; + return {}; +} + +Maybe<uint32_t> parseInt(const StringPiece& str) { + std::u16string str16 = util::utf8ToUtf16(str); + android::Res_value value; + if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { + return value.data; + } + return {}; } -Maybe<ResourceId> tryParseResourceId(const StringPiece& str) { +Maybe<ResourceId> parseResourceId(const StringPiece& str) { StringPiece trimmedStr(util::trimWhitespace(str)); std::u16string str16 = util::utf8ToUtf16(trimmedStr); @@ -452,7 +455,7 @@ Maybe<ResourceId> tryParseResourceId(const StringPiece& str) { return {}; } -Maybe<int> tryParseSdkVersion(const StringPiece& str) { +Maybe<int> parseSdkVersion(const StringPiece& str) { StringPiece trimmedStr(util::trimWhitespace(str)); std::u16string str16 = util::utf8ToUtf16(trimmedStr); @@ -470,12 +473,11 @@ Maybe<int> tryParseSdkVersion(const StringPiece& str) { } std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece& str) { - bool result = false; - if (tryParseBool(str, &result)) { + if (Maybe<bool> maybeResult = parseBool(str)) { android::Res_value value = {}; value.dataType = android::Res_value::TYPE_INT_BOOLEAN; - if (result) { + if (maybeResult.value()) { value.data = 0xffffffffu; } else { value.data = 0; @@ -542,7 +544,7 @@ uint32_t androidTypeToAttributeTypeMask(uint16_t type) { }; } -std::unique_ptr<Item> parseItemForAttribute( +std::unique_ptr<Item> tryParseItemForAttribute( const StringPiece& value, uint32_t typeMask, const std::function<void(const ResourceName&)>& onCreateReference) { @@ -602,11 +604,11 @@ std::unique_ptr<Item> parseItemForAttribute( * We successively try to parse the string as a resource type that the Attribute * allows. */ -std::unique_ptr<Item> parseItemForAttribute( +std::unique_ptr<Item> tryParseItemForAttribute( const StringPiece& str, const Attribute* attr, const std::function<void(const ResourceName&)>& onCreateReference) { const uint32_t typeMask = attr->typeMask; - std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, onCreateReference); + std::unique_ptr<Item> value = tryParseItemForAttribute(str, typeMask, onCreateReference); if (value) { return value; } diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h index a57d89dd0233..244047bae117 100644 --- a/tools/aapt2/ResourceUtils.h +++ b/tools/aapt2/ResourceUtils.h @@ -28,11 +28,6 @@ namespace aapt { namespace ResourceUtils { -/** - * Convert an android::ResTable::resource_name to an aapt::ResourceName struct. - */ -Maybe<ResourceName> toResourceName(const android::ResTable::resource_name& name); - /* * Extracts the package, type, and name from a string of the format: * @@ -60,8 +55,8 @@ bool parseResourceName(const StringPiece& str, ResourceNameRef* outResource, * If '+' was present in the reference, `outCreate` is set to true. * If '*' was present in the reference, `outPrivate` is set to true. */ -bool tryParseReference(const StringPiece& str, ResourceNameRef* outReference, - bool* outCreate = nullptr, bool* outPrivate = nullptr); +bool parseReference(const StringPiece& str, ResourceNameRef* outReference, + bool* outCreate = nullptr, bool* outPrivate = nullptr); /* * Returns true if the string is in the form of a resource reference (@[+][package:]type/name). @@ -72,7 +67,7 @@ bool isReference(const StringPiece& str); * Returns true if the string was parsed as an attribute reference (?[package:][type/]name), * with `outReference` set to the parsed reference. */ -bool tryParseAttributeReference(const StringPiece& str, ResourceNameRef* outReference); +bool parseAttributeReference(const StringPiece& str, ResourceNameRef* outReference); /** * Returns true if the string is in the form of an attribute reference(?[package:][type/]name). @@ -80,19 +75,29 @@ bool tryParseAttributeReference(const StringPiece& str, ResourceNameRef* outRefe bool isAttributeReference(const StringPiece& str); /** - * Returns true if the value is a boolean, putting the result in `outValue`. + * Convert an android::ResTable::resource_name to an aapt::ResourceName struct. + */ +Maybe<ResourceName> toResourceName(const android::ResTable::resource_name& name); + +/** + * Returns a boolean value if the string is equal to TRUE, true, True, FALSE, false, or False. + */ +Maybe<bool> parseBool(const StringPiece& str); + +/** + * Returns a uint32_t if the string is an integer. */ -bool tryParseBool(const StringPiece& str, bool* outValue); +Maybe<uint32_t> parseInt(const StringPiece& str); /** * Returns an ID if it the string represented a valid ID. */ -Maybe<ResourceId> tryParseResourceId(const StringPiece& str); +Maybe<ResourceId> parseResourceId(const StringPiece& str); /** * Parses an SDK version, which can be an integer, or a letter from A-Z. */ -Maybe<int> tryParseSdkVersion(const StringPiece& str); +Maybe<int> parseSdkVersion(const StringPiece& str); /* * Returns a Reference, or None Maybe instance if the string `str` was parsed as a @@ -161,11 +166,11 @@ std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* enumAttr, * The callback function onCreateReference is called when the parsed item is a * reference to an ID that must be created (@+id/foo). */ -std::unique_ptr<Item> parseItemForAttribute( +std::unique_ptr<Item> tryParseItemForAttribute( const StringPiece& value, const Attribute* attr, const std::function<void(const ResourceName&)>& onCreateReference = {}); -std::unique_ptr<Item> parseItemForAttribute( +std::unique_ptr<Item> tryParseItemForAttribute( const StringPiece& value, uint32_t typeMask, const std::function<void(const ResourceName&)>& onCreateReference = {}); diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp index fb76914cc495..894cfcf72144 100644 --- a/tools/aapt2/ResourceUtils_test.cpp +++ b/tools/aapt2/ResourceUtils_test.cpp @@ -21,24 +21,12 @@ namespace aapt { TEST(ResourceUtilsTest, ParseBool) { - bool val = false; - EXPECT_TRUE(ResourceUtils::tryParseBool("true", &val)); - EXPECT_TRUE(val); - - EXPECT_TRUE(ResourceUtils::tryParseBool("TRUE", &val)); - EXPECT_TRUE(val); - - EXPECT_TRUE(ResourceUtils::tryParseBool("True", &val)); - EXPECT_TRUE(val); - - EXPECT_TRUE(ResourceUtils::tryParseBool("false", &val)); - EXPECT_FALSE(val); - - EXPECT_TRUE(ResourceUtils::tryParseBool("FALSE", &val)); - EXPECT_FALSE(val); - - EXPECT_TRUE(ResourceUtils::tryParseBool("False", &val)); - EXPECT_FALSE(val); + EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("true")); + EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("TRUE")); + EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("True")); + EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("false")); + EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("FALSE")); + EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("False")); } TEST(ResourceUtilsTest, ParseResourceName) { @@ -64,7 +52,7 @@ TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) { ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference("@color/foo", &actual, &create, &privateRef)); + EXPECT_TRUE(ResourceUtils::parseReference("@color/foo", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_FALSE(create); EXPECT_FALSE(privateRef); @@ -75,7 +63,7 @@ TEST(ResourceUtilsTest, ParseReferenceWithPackage) { ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference("@android:color/foo", &actual, &create, + EXPECT_TRUE(ResourceUtils::parseReference("@android:color/foo", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_FALSE(create); @@ -87,7 +75,7 @@ TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) { ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference("\t @android:color/foo\n \n\t", &actual, + EXPECT_TRUE(ResourceUtils::parseReference("\t @android:color/foo\n \n\t", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_FALSE(create); @@ -99,7 +87,7 @@ TEST(ResourceUtilsTest, ParseAutoCreateIdReference) { ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference("@+android:id/foo", &actual, &create, + EXPECT_TRUE(ResourceUtils::parseReference("@+android:id/foo", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_TRUE(create); @@ -111,7 +99,7 @@ TEST(ResourceUtilsTest, ParsePrivateReference) { ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference("@*android:id/foo", &actual, &create, + EXPECT_TRUE(ResourceUtils::parseReference("@*android:id/foo", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_FALSE(create); @@ -122,7 +110,7 @@ TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) { bool create = false; bool privateRef = false; ResourceNameRef actual; - EXPECT_FALSE(ResourceUtils::tryParseReference("@+android:color/foo", &actual, &create, + EXPECT_FALSE(ResourceUtils::parseReference("@+android:color/foo", &actual, &create, &privateRef)); } diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp index 4a865799372d..73682ab110c7 100644 --- a/tools/aapt2/ResourceValues.cpp +++ b/tools/aapt2/ResourceValues.cpp @@ -449,9 +449,7 @@ void Attribute::print(std::ostream* out) const { printMask(out); if (!symbols.empty()) { - *out << " [" - << util::joiner(symbols.begin(), symbols.end(), ", ") - << "]"; + *out << " [" << util::joiner(symbols, ", ") << "]"; } if (minInt != std::numeric_limits<int32_t>::min()) { @@ -600,7 +598,7 @@ void Style::print(std::ostream* out) const { *out << parent.value().name.value(); } *out << " [" - << util::joiner(entries.begin(), entries.end(), ", ") + << util::joiner(entries, ", ") << "]"; } @@ -645,7 +643,7 @@ Array* Array::clone(StringPool* newPool) const { void Array::print(std::ostream* out) const { *out << "(array) [" - << util::joiner(items.begin(), items.end(), ", ") + << util::joiner(items, ", ") << "]"; } @@ -730,7 +728,7 @@ Styleable* Styleable::clone(StringPool* /*newPool*/) const { void Styleable::print(std::ostream* out) const { *out << "(styleable) " << " [" - << util::joiner(entries.begin(), entries.end(), ", ") + << util::joiner(entries, ", ") << "]"; } diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h index 319528e0ea1b..8a1021d49c39 100644 --- a/tools/aapt2/Source.h +++ b/tools/aapt2/Source.h @@ -35,7 +35,7 @@ struct Source { Source() = default; - inline Source(const StringPiece& path) : path(path.toString()) { + inline Source(const StringPiece& path) : path(path.toString()) { // NOLINT(implicit) } inline Source(const StringPiece& path, size_t line) : path(path.toString()), line(line) { diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h index b8bc5db7d6e4..9dc6a9c2b0d2 100644 --- a/tools/aapt2/ValueVisitor.h +++ b/tools/aapt2/ValueVisitor.h @@ -45,8 +45,9 @@ struct RawValueVisitor { virtual void visit(Styleable* value) {} }; +// NOLINT, do not add parentheses around T. #define DECL_VISIT_COMPOUND_VALUE(T) \ - virtual void visit(T* value) { \ + virtual void visit(T* value) { /* NOLINT */ \ visitSubValues(value); \ } diff --git a/tools/aapt2/compile/Png.h b/tools/aapt2/compile/Png.h index 345ff6c56870..f835b06e762b 100644 --- a/tools/aapt2/compile/Png.h +++ b/tools/aapt2/compile/Png.h @@ -32,7 +32,7 @@ struct PngOptions { class Png { public: - Png(IDiagnostics* diag) : mDiag(diag) { + explicit Png(IDiagnostics* diag) : mDiag(diag) { } bool process(const Source& source, std::istream* input, BigBuffer* outBuffer, diff --git a/tools/aapt2/compile/Pseudolocalizer.h b/tools/aapt2/compile/Pseudolocalizer.h index 7db88de13536..91d17d174d29 100644 --- a/tools/aapt2/compile/Pseudolocalizer.h +++ b/tools/aapt2/compile/Pseudolocalizer.h @@ -43,7 +43,7 @@ public: kBidi, }; - Pseudolocalizer(Method method); + explicit Pseudolocalizer(Method method); void setMethod(Method method); std::string start() { return mImpl->start(); } std::string end() { return mImpl->end(); } diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp index 9fc979c34353..3901419636b4 100644 --- a/tools/aapt2/compile/XmlIdCollector.cpp +++ b/tools/aapt2/compile/XmlIdCollector.cpp @@ -42,7 +42,7 @@ struct IdCollector : public xml::Visitor { for (xml::Attribute& attr : element->attributes) { ResourceNameRef name; bool create = false; - if (ResourceUtils::tryParseReference(attr.value, &name, &create, nullptr)) { + if (ResourceUtils::parseReference(attr.value, &name, &create, nullptr)) { if (create && name.type == ResourceType::kId) { auto iter = std::lower_bound(mOutSymbols->begin(), mOutSymbols->end(), name, cmpName); diff --git a/tools/aapt2/flatten/TableFlattener.h b/tools/aapt2/flatten/TableFlattener.h index 0ab01974044b..b416f202ced5 100644 --- a/tools/aapt2/flatten/TableFlattener.h +++ b/tools/aapt2/flatten/TableFlattener.h @@ -26,7 +26,7 @@ class ResourceTable; class TableFlattener : public IResourceTableConsumer { public: - TableFlattener(BigBuffer* buffer) : mBuffer(buffer) { + explicit TableFlattener(BigBuffer* buffer) : mBuffer(buffer) { } bool consume(IAaptContext* context, ResourceTable* table) override; diff --git a/tools/aapt2/io/FileSystem.h b/tools/aapt2/io/FileSystem.h index f0559c03a8b8..72a932a499dd 100644 --- a/tools/aapt2/io/FileSystem.h +++ b/tools/aapt2/io/FileSystem.h @@ -29,7 +29,7 @@ namespace io { */ class RegularFile : public IFile { public: - RegularFile(const Source& source); + explicit RegularFile(const Source& source); std::unique_ptr<IData> openAsData() override; const Source& getSource() const override; @@ -42,7 +42,7 @@ class FileCollection; class FileCollectionIterator : public IFileCollectionIterator { public: - FileCollectionIterator(FileCollection* collection); + explicit FileCollectionIterator(FileCollection* collection); bool hasNext() override; io::IFile* next() override; diff --git a/tools/aapt2/io/ZipArchive.h b/tools/aapt2/io/ZipArchive.h index 5ad119d1d6d4..565588e2db57 100644 --- a/tools/aapt2/io/ZipArchive.h +++ b/tools/aapt2/io/ZipArchive.h @@ -47,7 +47,7 @@ class ZipFileCollection; class ZipFileCollectionIterator : public IFileCollectionIterator { public: - ZipFileCollectionIterator(ZipFileCollection* collection); + explicit ZipFileCollectionIterator(ZipFileCollection* collection); bool hasNext() override; io::IFile* next() override; diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h index d45328fedba2..e84c2741e4ce 100644 --- a/tools/aapt2/java/ClassDefinition.h +++ b/tools/aapt2/java/ClassDefinition.h @@ -110,7 +110,7 @@ using StringMember = PrimitiveMember<std::string>; template <typename T> class PrimitiveArrayMember : public ClassMember { public: - PrimitiveArrayMember(const StringPiece& name) : + explicit PrimitiveArrayMember(const StringPiece& name) : mName(name.toString()) { } diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index ff777a3b60eb..ea95dd1e6c2f 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -59,11 +59,14 @@ struct LinkOptions { std::string manifestPath; std::vector<std::string> includePaths; std::vector<std::string> overlayFiles; + + // Java/Proguard options. Maybe<std::string> generateJavaClassPath; Maybe<std::string> customJavaPackage; std::set<std::string> extraJavaPackages; Maybe<std::string> generateProguardRulesPath; Maybe<std::string> generateMainDexProguardRulesPath; + bool noAutoVersion = false; bool noVersionVectors = false; bool staticLib = false; @@ -71,13 +74,20 @@ struct LinkOptions { bool generateNonFinalIds = false; std::vector<std::string> javadocAnnotations; bool outputToDirectory = false; + bool noXmlNamespaces = false; bool autoAddOverlay = false; bool doNotCompressAnything = false; std::unordered_set<std::string> extensionsToNotCompress; Maybe<std::string> privateSymbols; ManifestFixerOptions manifestFixerOptions; std::unordered_set<std::string> products; + + // Split APK options. TableSplitterOptions tableSplitterOptions; + std::vector<SplitConstraints> splitConstraints; + std::vector<std::string> splitPaths; + + // Stable ID options. std::unordered_map<ResourceName, ResourceId> stableIdMap; Maybe<std::string> resourceIdMapPath; }; @@ -284,6 +294,7 @@ static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source, struct ResourceFileFlattenerOptions { bool noAutoVersion = false; bool noVersionVectors = false; + bool noXmlNamespaces = false; bool keepRawValues = false; bool doNotCompressAnything = false; bool updateProguardSpec = false; @@ -373,6 +384,13 @@ bool ResourceFileFlattener::linkAndVersionXmlFile(const ResourceEntry* entry, return false; } + if (mOptions.noXmlNamespaces) { + XmlNamespaceRemover namespaceRemover; + if (!namespaceRemover.consume(mContext, outFileOp->xmlToFlatten.get())) { + return false; + } + } + if (!mOptions.noAutoVersion) { if (mOptions.noVersionVectors) { // Skip this if it is a vector or animated-vector. @@ -585,7 +603,7 @@ static bool loadStableIdMap(IDiagnostics* diag, const std::string& path, const size_t resIdStrLen = line.size() - resIdStartIdx; StringPiece resIdStr = util::trimWhitespace(line.substr(resIdStartIdx, resIdStrLen)); - Maybe<ResourceId> maybeId = ResourceUtils::tryParseResourceId(resIdStr); + Maybe<ResourceId> maybeId = ResourceUtils::parseResourceId(resIdStr); if (!maybeId) { diag->error(DiagMessage(Source(path, lineNo)) << "invalid resource ID '" << resIdStr << "'"); @@ -597,6 +615,28 @@ static bool loadStableIdMap(IDiagnostics* diag, const std::string& path, return true; } +static bool parseSplitParameter(const StringPiece& arg, IDiagnostics* diag, + std::string* outPath, SplitConstraints* outSplit) { + std::vector<std::string> parts = util::split(arg, ':'); + if (parts.size() != 2) { + diag->error(DiagMessage() << "invalid split parameter '" << arg << "'"); + diag->note(DiagMessage() << "should be --split path/to/output.apk:<config>[,<config>...]"); + return false; + } + *outPath = parts[0]; + std::vector<ConfigDescription> configs; + for (const StringPiece& configStr : util::tokenize(parts[1], ',')) { + configs.push_back({}); + if (!ConfigDescription::parse(configStr, &configs.back())) { + diag->error(DiagMessage() << "invalid config '" << configStr + << "' in split parameter '" << arg << "'"); + return false; + } + } + outSplit->configs.insert(configs.begin(), configs.end()); + return true; +} + class LinkCommand { public: LinkCommand(LinkContext* context, const LinkOptions& options) : @@ -676,6 +716,30 @@ public: appInfo.package = packageAttr->value; + if (xml::Attribute* versionCodeAttr = + manifestEl->findAttribute(xml::kSchemaAndroid, "versionCode")) { + Maybe<uint32_t> maybeCode = ResourceUtils::parseInt(versionCodeAttr->value); + if (!maybeCode) { + diag->error(DiagMessage(xmlRes->file.source.withLine(manifestEl->lineNumber)) + << "invalid android:versionCode '" + << versionCodeAttr->value << "'"); + return {}; + } + appInfo.versionCode = maybeCode.value(); + } + + if (xml::Attribute* revisionCodeAttr = + manifestEl->findAttribute(xml::kSchemaAndroid, "revisionCode")) { + Maybe<uint32_t> maybeCode = ResourceUtils::parseInt(revisionCodeAttr->value); + if (!maybeCode) { + diag->error(DiagMessage(xmlRes->file.source.withLine(manifestEl->lineNumber)) + << "invalid android:revisionCode '" + << revisionCodeAttr->value << "'"); + return {}; + } + appInfo.revisionCode = maybeCode.value(); + } + if (xml::Element* usesSdkEl = manifestEl->findChild({}, "uses-sdk")) { if (xml::Attribute* minSdk = usesSdkEl->findAttribute(xml::kSchemaAndroid, "minSdkVersion")) { @@ -767,11 +831,11 @@ public: return true; } - std::unique_ptr<IArchiveWriter> makeArchiveWriter() { + std::unique_ptr<IArchiveWriter> makeArchiveWriter(const StringPiece& out) { if (mOptions.outputToDirectory) { - return createDirectoryArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath); + return createDirectoryArchiveWriter(mContext->getDiagnostics(), out); } else { - return createZipFileArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath); + return createZipFileArchiveWriter(mContext->getDiagnostics(), out); } } @@ -1179,6 +1243,95 @@ public: return true; } + std::unique_ptr<xml::XmlResource> generateSplitManifest(const AppInfo& appInfo, + const SplitConstraints& constraints) { + std::unique_ptr<xml::XmlResource> doc = util::make_unique<xml::XmlResource>(); + + std::unique_ptr<xml::Namespace> namespaceAndroid = util::make_unique<xml::Namespace>(); + namespaceAndroid->namespaceUri = xml::kSchemaAndroid; + namespaceAndroid->namespacePrefix = "android"; + + std::unique_ptr<xml::Element> manifestEl = util::make_unique<xml::Element>(); + manifestEl->name = "manifest"; + manifestEl->attributes.push_back( + xml::Attribute{ "", "package", appInfo.package }); + + if (appInfo.versionCode) { + manifestEl->attributes.push_back(xml::Attribute{ + xml::kSchemaAndroid, + "versionCode", + std::to_string(appInfo.versionCode.value()) }); + } + + if (appInfo.revisionCode) { + manifestEl->attributes.push_back(xml::Attribute{ + xml::kSchemaAndroid, + "revisionCode", std::to_string(appInfo.revisionCode.value()) }); + } + + std::stringstream splitName; + splitName << "config." << util::joiner(constraints.configs, "_"); + + manifestEl->attributes.push_back( + xml::Attribute{ "", "split", splitName.str() }); + + std::unique_ptr<xml::Element> applicationEl = util::make_unique<xml::Element>(); + applicationEl->name = "application"; + applicationEl->attributes.push_back( + xml::Attribute{ xml::kSchemaAndroid, "hasCode", "false" }); + + manifestEl->addChild(std::move(applicationEl)); + namespaceAndroid->addChild(std::move(manifestEl)); + doc->root = std::move(namespaceAndroid); + return doc; + } + + /** + * Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable + * to the IArchiveWriter. + */ + bool writeApk(IArchiveWriter* writer, proguard::KeepSet* keepSet, xml::XmlResource* manifest, + ResourceTable* table) { + const bool keepRawValues = mOptions.staticLib; + bool result = flattenXml(manifest, "AndroidManifest.xml", {}, keepRawValues, writer, + mContext); + if (!result) { + return false; + } + + ResourceFileFlattenerOptions fileFlattenerOptions; + fileFlattenerOptions.keepRawValues = keepRawValues; + fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything; + fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress; + fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion; + fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors; + fileFlattenerOptions.noXmlNamespaces = mOptions.noXmlNamespaces; + fileFlattenerOptions.updateProguardSpec = + static_cast<bool>(mOptions.generateProguardRulesPath); + + ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext, keepSet); + + if (!fileFlattener.flatten(table, writer)) { + mContext->getDiagnostics()->error(DiagMessage() << "failed linking file resources"); + return false; + } + + if (mOptions.staticLib) { + if (!flattenTableToPb(table, writer)) { + mContext->getDiagnostics()->error(DiagMessage() + << "failed to write resources.arsc.flat"); + return false; + } + } else { + if (!flattenTable(table, writer)) { + mContext->getDiagnostics()->error(DiagMessage() + << "failed to write resources.arsc"); + return false; + } + } + return true; + } + int run(const std::vector<std::string>& inputFiles) { // Load the AndroidManifest.xml std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath, @@ -1187,30 +1340,33 @@ public: return 1; } + // First extract the Package name without modifying it (via --rename-manifest-package). if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(), mContext->getDiagnostics())) { - AppInfo& appInfo = maybeAppInfo.value(); + const AppInfo& appInfo = maybeAppInfo.value(); mContext->setCompilationPackage(appInfo.package); - if (appInfo.minSdkVersion) { - if (Maybe<int> maybeMinSdkVersion = - ResourceUtils::tryParseSdkVersion(appInfo.minSdkVersion.value())) { - mContext->setMinSdkVersion(maybeMinSdkVersion.value()); - } - } - } else { + } + + ManifestFixer manifestFixer(mOptions.manifestFixerOptions); + if (!manifestFixer.consume(mContext, manifestXml.get())) { return 1; } - if (!util::isJavaPackageName(mContext->getCompilationPackage())) { - mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath) - << "invalid package name '" - << mContext->getCompilationPackage() - << "'"); + Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(), + mContext->getDiagnostics()); + if (!maybeAppInfo) { return 1; } - mContext->setNameManglerPolicy(NameManglerPolicy{ mContext->getCompilationPackage() }); + const AppInfo& appInfo = maybeAppInfo.value(); + if (appInfo.minSdkVersion) { + if (Maybe<int> maybeMinSdkVersion = + ResourceUtils::parseSdkVersion(appInfo.minSdkVersion.value())) { + mContext->setMinSdkVersion(maybeMinSdkVersion.value()); + } + } + mContext->setNameManglerPolicy(NameManglerPolicy{ mContext->getCompilationPackage() }); if (mContext->getCompilationPackage() == "android") { mContext->setPackageId(0x01); } else { @@ -1258,9 +1414,7 @@ public: DiagMessage() << "failed moving private attributes"); return 1; } - } - if (!mOptions.staticLib) { // Assign IDs if we are building a regular app. IdAssigner idAssigner(&mOptions.stableIdMap); if (!idAssigner.consume(mContext, &mFinalTable)) { @@ -1304,45 +1458,118 @@ public: mContext->getExternalSymbols()->prependSource( util::make_unique<ResourceTableSymbolSource>(&mFinalTable)); - { - ReferenceLinker linker; - if (!linker.consume(mContext, &mFinalTable)) { - mContext->getDiagnostics()->error(DiagMessage() << "failed linking references"); + ReferenceLinker linker; + if (!linker.consume(mContext, &mFinalTable)) { + mContext->getDiagnostics()->error(DiagMessage() << "failed linking references"); + return 1; + } + + if (mOptions.staticLib) { + if (!mOptions.products.empty()) { + mContext->getDiagnostics()->warn( + DiagMessage() << "can't select products when building static library"); + } + } else { + ProductFilter productFilter(mOptions.products); + if (!productFilter.consume(mContext, &mFinalTable)) { + mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products"); + return 1; + } + } + + if (!mOptions.noAutoVersion) { + AutoVersioner versioner; + if (!versioner.consume(mContext, &mFinalTable)) { + mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles"); + return 1; + } + } + + if (!mOptions.staticLib && mContext->getMinSdkVersion() > 0) { + if (mContext->verbose()) { + mContext->getDiagnostics()->note( + DiagMessage() << "collapsing resource versions for minimum SDK " + << mContext->getMinSdkVersion()); + } + + VersionCollapser collapser; + if (!collapser.consume(mContext, &mFinalTable)) { + return 1; + } + } + + proguard::KeepSet proguardKeepSet; + proguard::KeepSet proguardMainDexKeepSet; + + if (mOptions.staticLib) { + if (mOptions.tableSplitterOptions.configFilter != nullptr || + mOptions.tableSplitterOptions.preferredDensity) { + mContext->getDiagnostics()->warn( + DiagMessage() << "can't strip resources when building static library"); + } + } else { + // Adjust the SplitConstraints so that their SDK version is stripped if it is less + // than or equal to the minSdk. Otherwise the resources that have had their SDK version + // stripped due to minSdk won't ever match. + std::vector<SplitConstraints> adjustedConstraintsList; + adjustedConstraintsList.reserve(mOptions.splitConstraints.size()); + for (const SplitConstraints& constraints : mOptions.splitConstraints) { + SplitConstraints adjustedConstraints; + for (const ConfigDescription& config : constraints.configs) { + if (config.sdkVersion <= mContext->getMinSdkVersion()) { + adjustedConstraints.configs.insert(config.copyWithoutSdkVersion()); + } else { + adjustedConstraints.configs.insert(config); + } + } + adjustedConstraintsList.push_back(std::move(adjustedConstraints)); + } + + TableSplitter tableSplitter(adjustedConstraintsList, mOptions.tableSplitterOptions); + if (!tableSplitter.verifySplitConstraints(mContext)) { return 1; } + tableSplitter.splitTable(&mFinalTable); - if (mOptions.staticLib) { - if (!mOptions.products.empty()) { - mContext->getDiagnostics()->warn( - DiagMessage() << "can't select products when building static library"); + // Now we need to write out the Split APKs. + auto pathIter = mOptions.splitPaths.begin(); + auto splitConstraintsIter = adjustedConstraintsList.begin(); + for (std::unique_ptr<ResourceTable>& splitTable : tableSplitter.getSplits()) { + if (mContext->verbose()) { + mContext->getDiagnostics()->note( + DiagMessage(*pathIter) << "generating split with configurations '" + << util::joiner(splitConstraintsIter->configs, ", ") << "'"); } - if (mOptions.tableSplitterOptions.configFilter != nullptr || - mOptions.tableSplitterOptions.preferredDensity) { - mContext->getDiagnostics()->warn( - DiagMessage() << "can't strip resources when building static library"); + std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter(*pathIter); + if (!archiveWriter) { + mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive"); + return 1; } - } else { - ProductFilter productFilter(mOptions.products); - if (!productFilter.consume(mContext, &mFinalTable)) { - mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products"); + + // Generate an AndroidManifest.xml for each split. + std::unique_ptr<xml::XmlResource> splitManifest = + generateSplitManifest(appInfo, *splitConstraintsIter); + + XmlReferenceLinker linker; + if (!linker.consume(mContext, splitManifest.get())) { + mContext->getDiagnostics()->error( + DiagMessage() << "failed to create Split AndroidManifest.xml"); return 1; } - // TODO(adamlesinski): Actually pass in split constraints and handle splits at the file - // level. - TableSplitter tableSplitter({}, mOptions.tableSplitterOptions); - if (!tableSplitter.verifySplitConstraints(mContext)) { + if (!writeApk(archiveWriter.get(), &proguardKeepSet, splitManifest.get(), + splitTable.get())) { return 1; } - tableSplitter.splitTable(&mFinalTable); + + ++pathIter; + ++splitConstraintsIter; } } - proguard::KeepSet proguardKeepSet; - proguard::KeepSet proguardMainDexKeepSet; - - std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter(); + // Start writing the base APK. + std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter(mOptions.outputPath); if (!archiveWriter) { mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive"); return 1; @@ -1350,11 +1577,6 @@ public: bool error = false; { - ManifestFixer manifestFixer(mOptions.manifestFixerOptions); - if (!manifestFixer.consume(mContext, manifestXml.get())) { - error = true; - } - // AndroidManifest.xml has no resource name, but the CallSite is built from the name // (aka, which package the AndroidManifest.xml is coming from). // So we give it a package name so it can see local resources. @@ -1383,11 +1605,12 @@ public: } } - const bool keepRawValues = mOptions.staticLib; - bool result = flattenXml(manifestXml.get(), "AndroidManifest.xml", {}, - keepRawValues, archiveWriter.get(), mContext); - if (!result) { - error = true; + if (mOptions.noXmlNamespaces) { + // PackageParser will fail if URIs are removed from AndroidManifest.xml. + XmlNamespaceRemover namespaceRemover(true /* keepUris */); + if (!namespaceRemover.consume(mContext, manifestXml.get())) { + error = true; + } } } else { error = true; @@ -1399,58 +1622,10 @@ public: return 1; } - if (!mOptions.noAutoVersion) { - AutoVersioner versioner; - if (!versioner.consume(mContext, &mFinalTable)) { - mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles"); - return 1; - } - } - - if (!mOptions.staticLib && mContext->getMinSdkVersion() > 0) { - if (mContext->verbose()) { - mContext->getDiagnostics()->note( - DiagMessage() << "collapsing resource versions for minimum SDK " - << mContext->getMinSdkVersion()); - } - - VersionCollapser collapser; - if (!collapser.consume(mContext, &mFinalTable)) { - return 1; - } - } - - // Write out the table to an archive. Optimizations to the table should come before this - // step. - ResourceFileFlattenerOptions fileFlattenerOptions; - fileFlattenerOptions.keepRawValues = mOptions.staticLib; - fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything; - fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress; - fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion; - fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors; - fileFlattenerOptions.updateProguardSpec = - static_cast<bool>(mOptions.generateProguardRulesPath); - ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext, &proguardKeepSet); - - if (!fileFlattener.flatten(&mFinalTable, archiveWriter.get())) { - mContext->getDiagnostics()->error(DiagMessage() << "failed linking file resources"); + if (!writeApk(archiveWriter.get(), &proguardKeepSet, manifestXml.get(), &mFinalTable)) { return 1; } - if (mOptions.staticLib) { - if (!flattenTableToPb(&mFinalTable, archiveWriter.get())) { - mContext->getDiagnostics()->error(DiagMessage() - << "failed to write resources.arsc.flat"); - return 1; - } - } else { - if (!flattenTable(&mFinalTable, archiveWriter.get())) { - mContext->getDiagnostics()->error(DiagMessage() - << "failed to write resources.arsc"); - return 1; - } - } - if (mOptions.generateJavaClassPath) { JavaClassGeneratorOptions options; options.types = JavaClassGeneratorOptions::SymbolTypes::kAll; @@ -1538,6 +1713,7 @@ int link(const std::vector<StringPiece>& args) { bool requireLocalization = false; bool verbose = false; Maybe<std::string> stableIdFilePath; + std::vector<std::string> splitArgs; Flags flags = Flags() .requiredFlag("-o", "Output path", &options.outputPath) .requiredFlag("--manifest", "Path to the Android manifest to build", @@ -1574,6 +1750,9 @@ int link(const std::vector<StringPiece>& args) { .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified " "by -o", &options.outputToDirectory) + .optionalSwitch("--no-xml-namespaces", "Removes XML namespace prefix and URI " + "information from AndroidManifest.xml\nand XML binaries in res/*.", + &options.noXmlNamespaces) .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for " "AndroidManifest.xml", &options.manifestFixerOptions.minSdkVersionDefault) @@ -1623,6 +1802,9 @@ int link(const std::vector<StringPiece>& args) { &options.manifestFixerOptions.renameInstrumentationTargetPackage) .optionalFlagList("-0", "File extensions not to compress", &options.extensionsToNotCompress) + .optionalFlagList("--split", "Split resources matching a set of configs out to a " + "Split APK.\nSyntax: path/to/output.apk:<config>[,<config>[...]]", + &splitArgs) .optionalSwitch("-v", "Enables verbose logging", &verbose); @@ -1741,6 +1923,16 @@ int link(const std::vector<StringPiece>& args) { ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"}); + // Parse the split parameters. + for (const std::string& splitArg : splitArgs) { + options.splitPaths.push_back({}); + options.splitConstraints.push_back({}); + if (!parseSplitParameter(splitArg, context.getDiagnostics(), &options.splitPaths.back(), + &options.splitConstraints.back())) { + return 1; + } + } + // Turn off auto versioning for static-libs. if (options.staticLib) { options.noAutoVersion = true; diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h index 43b8fb494f2c..82e28682f78e 100644 --- a/tools/aapt2/link/Linkers.h +++ b/tools/aapt2/link/Linkers.h @@ -86,6 +86,23 @@ struct PrivateAttributeMover : public IResourceTableConsumer { }; /** + * Removes namespace nodes and URI information from the XmlResource. + * + * Once an XmlResource is processed by this consumer, it is no longer able to have its attributes + * parsed. As such, this XmlResource must have already been processed by XmlReferenceLinker. + */ +class XmlNamespaceRemover : public IXmlResourceConsumer { +private: + bool mKeepUris; + +public: + XmlNamespaceRemover(bool keepUris = false) : mKeepUris(keepUris) { + }; + + bool consume(IAaptContext* context, xml::XmlResource* resource) override; +}; + +/** * Resolves attributes in the XmlResource and compiles string values to resource values. * Once an XmlResource is processed by this linker, it is ready to be flattened. */ diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h index 55b587e57013..2e81266015e1 100644 --- a/tools/aapt2/link/ManifestFixer.h +++ b/tools/aapt2/link/ManifestFixer.h @@ -41,7 +41,7 @@ struct ManifestFixerOptions { */ class ManifestFixer : public IXmlResourceConsumer { public: - ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) { + explicit ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) { } bool consume(IAaptContext* context, xml::XmlResource* doc) override; diff --git a/tools/aapt2/link/ProductFilter.h b/tools/aapt2/link/ProductFilter.h index d2edd38289dc..7724e140427b 100644 --- a/tools/aapt2/link/ProductFilter.h +++ b/tools/aapt2/link/ProductFilter.h @@ -29,7 +29,7 @@ class ProductFilter { public: using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator; - ProductFilter(std::unordered_set<std::string> products) : mProducts(products) { } + explicit ProductFilter(std::unordered_set<std::string> products) : mProducts(products) { } ResourceConfigValueIter selectProductToKeep(const ResourceNameRef& name, const ResourceConfigValueIter begin, diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp index 6aa9c0edd059..be7aca3ca49f 100644 --- a/tools/aapt2/link/ReferenceLinker.cpp +++ b/tools/aapt2/link/ReferenceLinker.cpp @@ -138,7 +138,7 @@ private: const Attribute* attr) { if (RawString* rawString = valueCast<RawString>(value.get())) { std::unique_ptr<Item> transformed = - ResourceUtils::parseItemForAttribute(*rawString->value, attr); + ResourceUtils::tryParseItemForAttribute(*rawString->value, attr); // If we could not parse as any specific type, try a basic STRING. if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) { diff --git a/tools/aapt2/link/XmlNamespaceRemover.cpp b/tools/aapt2/link/XmlNamespaceRemover.cpp new file mode 100644 index 000000000000..9f95177537ce --- /dev/null +++ b/tools/aapt2/link/XmlNamespaceRemover.cpp @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#include "ResourceTable.h" +#include "link/Linkers.h" + +#include <algorithm> + +namespace aapt { + +namespace { + +/** + * Visits each xml Node, removing URI references and nested namespaces. + */ +class XmlVisitor : public xml::Visitor { +public: + XmlVisitor(bool keepUris) : mKeepUris(keepUris) { + } + + void visit(xml::Element* el) override { + // Strip namespaces + for (auto& child : el->children) { + while (child && xml::nodeCast<xml::Namespace>(child.get())) { + if (child->children.empty()) { + child = {}; + } else { + child = std::move(child->children.front()); + child->parent = el; + } + } + } + el->children.erase(std::remove_if(el->children.begin(), el->children.end(), + [](const std::unique_ptr<xml::Node>& child) -> bool { + return child == nullptr; + }), el->children.end()); + + if (!mKeepUris) { + for (xml::Attribute& attr : el->attributes) { + attr.namespaceUri = std::string(); + } + el->namespaceUri = std::string(); + } + xml::Visitor::visit(el); + } + +private: + bool mKeepUris; +}; + +} // namespace + +bool XmlNamespaceRemover::consume(IAaptContext* context, xml::XmlResource* resource) { + if (!resource->root) { + return false; + } + // Replace any root namespaces until the root is a non-namespace node + while (xml::nodeCast<xml::Namespace>(resource->root.get())) { + if (resource->root->children.empty()) { + break; + } + resource->root = std::move(resource->root->children.front()); + resource->root->parent = nullptr; + } + XmlVisitor visitor(mKeepUris); + resource->root->accept(&visitor); + return true; +} + +} // namespace aapt diff --git a/tools/aapt2/link/XmlNamespaceRemover_test.cpp b/tools/aapt2/link/XmlNamespaceRemover_test.cpp new file mode 100644 index 000000000000..e72ea4391707 --- /dev/null +++ b/tools/aapt2/link/XmlNamespaceRemover_test.cpp @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#include "link/Linkers.h" +#include "test/Test.h" + +namespace aapt { + +class XmlUriTestVisitor : public xml::Visitor { +public: + void visit(xml::Element* el) override { + for (const auto& attr : el->attributes) { + EXPECT_EQ(std::string(), attr.namespaceUri); + } + EXPECT_EQ(std::string(), el->namespaceUri); + xml::Visitor::visit(el); + } + + void visit(xml::Namespace* ns) override { + EXPECT_EQ(std::string(), ns->namespaceUri); + xml::Visitor::visit(ns); + } +}; + +class XmlNamespaceTestVisitor : public xml::Visitor { +public: + void visit(xml::Namespace* ns) override { + ADD_FAILURE() << "Detected namespace: " + << ns->namespacePrefix << "=\"" << ns->namespaceUri << "\""; + xml::Visitor::visit(ns); + } +}; + +class XmlNamespaceRemoverTest : public ::testing::Test { +public: + void SetUp() override { + mContext = test::ContextBuilder() + .setCompilationPackage("com.app.test") + .build(); + } + +protected: + std::unique_ptr<IAaptContext> mContext; +}; + +TEST_F(XmlNamespaceRemoverTest, RemoveUris) { + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( + <View xmlns:android="http://schemas.android.com/apk/res/android" + android:text="hello" />)EOF"); + + XmlNamespaceRemover remover; + ASSERT_TRUE(remover.consume(mContext.get(), doc.get())); + + xml::Node* root = doc.get()->root.get(); + ASSERT_NE(root, nullptr); + + XmlUriTestVisitor visitor; + root->accept(&visitor); +} + +TEST_F(XmlNamespaceRemoverTest, RemoveNamespaces) { + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( + <View xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:foo="http://schemas.android.com/apk/res/foo" + foo:bar="foobar" + android:text="hello" />)EOF"); + + XmlNamespaceRemover remover; + ASSERT_TRUE(remover.consume(mContext.get(), doc.get())); + + xml::Node* root = doc.get()->root.get(); + ASSERT_NE(root, nullptr); + + XmlNamespaceTestVisitor visitor; + root->accept(&visitor); +} + +TEST_F(XmlNamespaceRemoverTest, RemoveNestedNamespaces) { + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( + <View xmlns:android="http://schemas.android.com/apk/res/android" + android:text="hello"> + <View xmlns:foo="http://schemas.example.com/foo" + android:text="foo"/> + </View>)EOF"); + + XmlNamespaceRemover remover; + ASSERT_TRUE(remover.consume(mContext.get(), doc.get())); + + xml::Node* root = doc.get()->root.get(); + ASSERT_NE(root, nullptr); + + XmlNamespaceTestVisitor visitor; + root->accept(&visitor); +} + +} // namespace aapt diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp index a29d8dcfe3c6..59ffe15fd4a4 100644 --- a/tools/aapt2/link/XmlReferenceLinker.cpp +++ b/tools/aapt2/link/XmlReferenceLinker.cpp @@ -106,7 +106,7 @@ public: } const Attribute* attribute = &attr.compiledAttribute.value().attribute; - attr.compiledValue = ResourceUtils::parseItemForAttribute(attr.value, + attr.compiledValue = ResourceUtils::tryParseItemForAttribute(attr.value, attribute); if (!attr.compiledValue && !(attribute->typeMask & android::ResTable_map::TYPE_STRING)) { diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h index bd01b64281d5..bd31416a5cee 100644 --- a/tools/aapt2/process/SymbolTable.h +++ b/tools/aapt2/process/SymbolTable.h @@ -54,7 +54,7 @@ public: Symbol() : Symbol(Maybe<ResourceId>{}) { } - Symbol(const Maybe<ResourceId>& i) : Symbol(i, nullptr) { + explicit Symbol(const Maybe<ResourceId>& i) : Symbol(i, nullptr) { } Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr) : diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md index 95c0173f921a..a68d6f67324f 100644 --- a/tools/aapt2/readme.md +++ b/tools/aapt2/readme.md @@ -1,5 +1,14 @@ # Android Asset Packaging Tool 2.0 (AAPT2) release notes +## Version 2.1 +### `aapt2 link ...` +- Configuration Split APK support: supports splitting resources that match a set of + configurations to a separate APK which can be loaded alongside the base APK on + API 21+ devices. This is done using the flag + `--split path/to/split.apk:<config1>[,<config2>,...]`. +- SDK version resource filtering: Resources with an SDK version qualifier that is unreachable + at runtime due to the minimum SDK level declared by the AndroidManifest.xml are stripped. + ## Version 2.0 ### `aapt2 compile ...` - Pseudo-localization: generates pseudolocalized versions of default strings when the diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp index 2dfe2a239900..08b9ee9cbe1b 100644 --- a/tools/aapt2/split/TableSplitter.cpp +++ b/tools/aapt2/split/TableSplitter.cpp @@ -17,6 +17,7 @@ #include "ConfigDescription.h" #include "ResourceTable.h" #include "split/TableSplitter.h" +#include "util/Util.h" #include <algorithm> #include <map> @@ -76,7 +77,6 @@ public: // in multiple splits. const ConfigDescription& config = entry.first; const std::vector<ResourceConfigValue*>& relatedValues = entry.second; - auto densityValueIter = mDensityDependentConfigToDensityMap.find(config); if (densityValueIter != mDensityDependentConfigToDensityMap.end()) { // Select the best one! @@ -89,12 +89,12 @@ public: thisValue->config.isBetterThan(bestValue->config, &targetDensity)) { bestValue = thisValue; } - - // When we select one of these, they are all claimed such that the base - // doesn't include any anymore. - (*claimedValues)[thisValue] = true; } assert(bestValue); + + // When we select one of these, they are all claimed such that the base + // doesn't include any anymore. + (*claimedValues)[bestValue] = true; selected.push_back(bestValue); } } @@ -135,7 +135,6 @@ static void markNonPreferredDensitiesAsClaimed(uint16_t preferredDensity, assert(bestValue); } } - bool TableSplitter::verifySplitConstraints(IAaptContext* context) { bool error = false; for (size_t i = 0; i < mSplitConstraints.size(); i++) { diff --git a/tools/aapt2/split/TableSplitter.h b/tools/aapt2/split/TableSplitter.h index 15e0764c4259..2fa5c47d5bd6 100644 --- a/tools/aapt2/split/TableSplitter.h +++ b/tools/aapt2/split/TableSplitter.h @@ -60,7 +60,7 @@ public: void splitTable(ResourceTable* originalTable); - const std::vector<std::unique_ptr<ResourceTable>>& getSplits() { + std::vector<std::unique_ptr<ResourceTable>>& getSplits() { return mSplits; } diff --git a/tools/aapt2/split/TableSplitter_test.cpp b/tools/aapt2/split/TableSplitter_test.cpp index bad02a5cb332..5150e82b6d93 100644 --- a/tools/aapt2/split/TableSplitter_test.cpp +++ b/tools/aapt2/split/TableSplitter_test.cpp @@ -52,6 +52,71 @@ TEST(TableSplitterTest, NoSplitPreferredDensity) { EXPECT_NE(nullptr, test::getValue<Id>(table.get(), "android:string/one")); } +TEST(TableSplitterTest, SplitTableByDensity) { + std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() + .addFileReference("android:drawable/foo", "res/drawable-mdpi/foo.png", + test::parseConfigOrDie("mdpi")) + .addFileReference("android:drawable/foo", "res/drawable-hdpi/foo.png", + test::parseConfigOrDie("hdpi")) + .addFileReference("android:drawable/foo", "res/drawable-xhdpi/foo.png", + test::parseConfigOrDie("xhdpi")) + .addFileReference("android:drawable/foo", "res/drawable-xxhdpi/foo.png", + test::parseConfigOrDie("xxhdpi")) + .build(); + + std::vector<SplitConstraints> constraints; + constraints.push_back(SplitConstraints{ { test::parseConfigOrDie("mdpi") } }); + constraints.push_back(SplitConstraints{ { test::parseConfigOrDie("hdpi") } }); + constraints.push_back(SplitConstraints{ { test::parseConfigOrDie("xhdpi") } }); + + TableSplitter splitter(constraints, TableSplitterOptions{}); + splitter.splitTable(table.get()); + + ASSERT_EQ(3u, splitter.getSplits().size()); + + ResourceTable* splitOne = splitter.getSplits()[0].get(); + ResourceTable* splitTwo = splitter.getSplits()[1].get(); + ResourceTable* splitThree = splitter.getSplits()[2].get(); + + // Just xxhdpi should be in the base. + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(table.get(), "android:drawable/foo", + test::parseConfigOrDie("mdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(table.get(), "android:drawable/foo", + test::parseConfigOrDie("hdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(table.get(), "android:drawable/foo", + test::parseConfigOrDie("xhdpi"))); + EXPECT_NE(nullptr, test::getValueForConfig<FileReference>(table.get(), "android:drawable/foo", + test::parseConfigOrDie("xxhdpi"))); + + // Each split should have one and only one drawable. + EXPECT_NE(nullptr, test::getValueForConfig<FileReference>(splitOne, "android:drawable/foo", + test::parseConfigOrDie("mdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitOne, "android:drawable/foo", + test::parseConfigOrDie("hdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitOne, "android:drawable/foo", + test::parseConfigOrDie("xhdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitOne, "android:drawable/foo", + test::parseConfigOrDie("xxhdpi"))); + + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitTwo, "android:drawable/foo", + test::parseConfigOrDie("mdpi"))); + EXPECT_NE(nullptr, test::getValueForConfig<FileReference>(splitTwo, "android:drawable/foo", + test::parseConfigOrDie("hdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitTwo, "android:drawable/foo", + test::parseConfigOrDie("xhdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitTwo, "android:drawable/foo", + test::parseConfigOrDie("xxhdpi"))); + + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitThree, "android:drawable/foo", + test::parseConfigOrDie("mdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitThree, "android:drawable/foo", + test::parseConfigOrDie("hdpi"))); + EXPECT_NE(nullptr, test::getValueForConfig<FileReference>(splitThree, "android:drawable/foo", + test::parseConfigOrDie("xhdpi"))); + EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(splitThree, "android:drawable/foo", + test::parseConfigOrDie("xxhdpi"))); +} + TEST(TableSplitterTest, SplitTableByConfigAndDensity) { ResourceTable table; @@ -78,12 +143,12 @@ TEST(TableSplitterTest, SplitTableByConfigAndDensity) { ResourceTable* splitOne = splitter.getSplits()[0].get(); ResourceTable* splitTwo = splitter.getSplits()[1].get(); - // Since a split was defined, all densities should be gone from base. + // All but the xxhdpi resource should be gone, since there were closer matches in land-xhdpi. EXPECT_EQ(nullptr, test::getValueForConfig<Id>(&table, "android:string/foo", test::parseConfigOrDie("land-hdpi"))); EXPECT_EQ(nullptr, test::getValueForConfig<Id>(&table, "android:string/foo", test::parseConfigOrDie("land-xhdpi"))); - EXPECT_EQ(nullptr, test::getValueForConfig<Id>(&table, "android:string/foo", + EXPECT_NE(nullptr, test::getValueForConfig<Id>(&table, "android:string/foo", test::parseConfigOrDie("land-xxhdpi"))); EXPECT_NE(nullptr, test::getValueForConfig<Id>(splitOne, "android:string/foo", diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h index c0c016032921..637e991a573f 100644 --- a/tools/aapt2/test/Builders.h +++ b/tools/aapt2/test/Builders.h @@ -46,12 +46,12 @@ public: return *this; } - ResourceTableBuilder& addSimple(const StringPiece& name, const ResourceId id = {}) { + ResourceTableBuilder& addSimple(const StringPiece& name, const ResourceId& id = {}) { return addValue(name, id, util::make_unique<Id>()); } ResourceTableBuilder& addSimple(const StringPiece& name, const ConfigDescription& config, - const ResourceId id = {}) { + const ResourceId& id = {}) { return addValue(name, config, id, util::make_unique<Id>()); } @@ -59,7 +59,7 @@ public: return addReference(name, {}, ref); } - ResourceTableBuilder& addReference(const StringPiece& name, const ResourceId id, + ResourceTableBuilder& addReference(const StringPiece& name, const ResourceId& id, const StringPiece& ref) { return addValue(name, id, util::make_unique<Reference>(parseNameOrDie(ref))); } @@ -68,12 +68,12 @@ public: return addString(name, {}, str); } - ResourceTableBuilder& addString(const StringPiece& name, const ResourceId id, + ResourceTableBuilder& addString(const StringPiece& name, const ResourceId& id, const StringPiece& str) { return addValue(name, id, util::make_unique<String>(mTable->stringPool.makeRef(str))); } - ResourceTableBuilder& addString(const StringPiece& name, const ResourceId id, + ResourceTableBuilder& addString(const StringPiece& name, const ResourceId& id, const ConfigDescription& config, const StringPiece& str) { return addValue(name, config, id, util::make_unique<String>(mTable->stringPool.makeRef(str))); @@ -83,7 +83,7 @@ public: return addFileReference(name, {}, path); } - ResourceTableBuilder& addFileReference(const StringPiece& name, const ResourceId id, + ResourceTableBuilder& addFileReference(const StringPiece& name, const ResourceId& id, const StringPiece& path) { return addValue(name, id, util::make_unique<FileReference>(mTable->stringPool.makeRef(path))); @@ -100,13 +100,13 @@ public: return addValue(name, {}, std::move(value)); } - ResourceTableBuilder& addValue(const StringPiece& name, const ResourceId id, + ResourceTableBuilder& addValue(const StringPiece& name, const ResourceId& id, std::unique_ptr<Value> value) { return addValue(name, {}, id, std::move(value)); } ResourceTableBuilder& addValue(const StringPiece& name, const ConfigDescription& config, - const ResourceId id, std::unique_ptr<Value> value) { + const ResourceId& id, std::unique_ptr<Value> value) { ResourceName resName = parseNameOrDie(name); bool result = mTable->addResourceAllowMangled(resName, id, config, {}, std::move(value), &mDiagnostics); @@ -114,7 +114,7 @@ public: return *this; } - ResourceTableBuilder& setSymbolState(const StringPiece& name, ResourceId id, + ResourceTableBuilder& setSymbolState(const StringPiece& name, const ResourceId& id, SymbolState state) { ResourceName resName = parseNameOrDie(name); Symbol symbol; @@ -130,7 +130,7 @@ public: }; inline std::unique_ptr<Reference> buildReference(const StringPiece& ref, - Maybe<ResourceId> id = {}) { + const Maybe<ResourceId>& id = {}) { std::unique_ptr<Reference> reference = util::make_unique<Reference>(parseNameOrDie(ref)); reference->id = id; return reference; @@ -151,7 +151,7 @@ private: public: template <typename... Args> - ValueBuilder(Args&&... args) : mValue(new T{ std::forward<Args>(args)... }) { + explicit ValueBuilder(Args&&... args) : mValue(new T{ std::forward<Args>(args)... }) { } template <typename... Args> @@ -175,7 +175,7 @@ private: std::unique_ptr<Attribute> mAttr; public: - AttributeBuilder(bool weak = false) : mAttr(util::make_unique<Attribute>(weak)) { + explicit AttributeBuilder(bool weak = false) : mAttr(util::make_unique<Attribute>(weak)) { mAttr->typeMask = android::ResTable_map::TYPE_ANY; } @@ -211,7 +211,7 @@ public: return *this; } - StyleBuilder& addItem(const StringPiece& str, ResourceId id, std::unique_ptr<Item> value) { + StyleBuilder& addItem(const StringPiece& str, const ResourceId& id, std::unique_ptr<Item> value) { addItem(str, std::move(value)); mStyle->entries.back().key.id = id; return *this; @@ -227,7 +227,7 @@ private: std::unique_ptr<Styleable> mStyleable = util::make_unique<Styleable>(); public: - StyleableBuilder& addItem(const StringPiece& str, Maybe<ResourceId> id = {}) { + StyleableBuilder& addItem(const StringPiece& str, const Maybe<ResourceId>& id = {}) { mStyleable->entries.push_back(Reference(parseNameOrDie(str))); mStyleable->entries.back().id = id; return *this; diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h index 624a71a382c9..7fafcbeb1a04 100644 --- a/tools/aapt2/test/Common.h +++ b/tools/aapt2/test/Common.h @@ -104,7 +104,7 @@ private: Source mSource; public: - TestFile(const StringPiece& path) : mSource(path) {} + explicit TestFile(const StringPiece& path) : mSource(path) {} std::unique_ptr<io::IData> openAsData() override { return {}; diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h index b053e07eafde..54f16db3d2f9 100644 --- a/tools/aapt2/test/Context.h +++ b/tools/aapt2/test/Context.h @@ -87,7 +87,7 @@ public: return *this; } - ContextBuilder& setNameManglerPolicy(NameManglerPolicy policy) { + ContextBuilder& setNameManglerPolicy(const NameManglerPolicy& policy) { mContext->mNameMangler = NameMangler(policy); return *this; } diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h index cad2a2e63be1..ba8532f829e6 100644 --- a/tools/aapt2/util/BigBuffer.h +++ b/tools/aapt2/util/BigBuffer.h @@ -63,7 +63,7 @@ public: * Create a BigBuffer with block allocation sizes * of blockSize. */ - BigBuffer(size_t blockSize); + explicit BigBuffer(size_t blockSize); BigBuffer(const BigBuffer&) = delete; // No copying. diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h index 4d8a1feb63b1..52c2052aec3d 100644 --- a/tools/aapt2/util/Files.h +++ b/tools/aapt2/util/Files.h @@ -109,7 +109,7 @@ bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outAr */ class FileFilter { public: - FileFilter(IDiagnostics* diag) : mDiag(diag) { + explicit FileFilter(IDiagnostics* diag) : mDiag(diag) { } /* diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h index a31bc89fa98b..129f6d9d9716 100644 --- a/tools/aapt2/util/Maybe.h +++ b/tools/aapt2/util/Maybe.h @@ -43,12 +43,12 @@ public: Maybe(const Maybe& rhs); template <typename U> - Maybe(const Maybe<U>& rhs); + Maybe(const Maybe<U>& rhs); // NOLINT(implicit) Maybe(Maybe&& rhs); template <typename U> - Maybe(Maybe<U>&& rhs); + Maybe(Maybe<U>&& rhs); // NOLINT(implicit) Maybe& operator=(const Maybe& rhs); @@ -63,12 +63,12 @@ public: /** * Construct a Maybe holding a value. */ - Maybe(const T& value); + Maybe(const T& value); // NOLINT(implicit) /** * Construct a Maybe holding a value. */ - Maybe(T&& value); + Maybe(T&& value); // NOLINT(implicit) /** * True if this holds a value, false if diff --git a/tools/aapt2/util/StringPiece.h b/tools/aapt2/util/StringPiece.h index f5c985bd9fff..4300a67d3581 100644 --- a/tools/aapt2/util/StringPiece.h +++ b/tools/aapt2/util/StringPiece.h @@ -41,8 +41,8 @@ public: BasicStringPiece(); BasicStringPiece(const BasicStringPiece<TChar>& str); - BasicStringPiece(const std::basic_string<TChar>& str); - BasicStringPiece(const TChar* str); + BasicStringPiece(const std::basic_string<TChar>& str); // NOLINT(implicit) + BasicStringPiece(const TChar* str); // NOLINT(implicit) BasicStringPiece(const TChar* str, size_t len); BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs); diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h index 4a10987b4400..998ecf7702bd 100644 --- a/tools/aapt2/util/Util.h +++ b/tools/aapt2/util/Util.h @@ -101,12 +101,16 @@ std::unique_ptr<T> make_unique(Args&&... args) { * Writes a set of items to the std::ostream, joining the times with the provided * separator. */ -template <typename Iterator> -::std::function<::std::ostream&(::std::ostream&)> joiner(Iterator begin, Iterator end, - const char* sep) { - return [begin, end, sep](::std::ostream& out) -> ::std::ostream& { - for (auto iter = begin; iter != end; ++iter) { - if (iter != begin) { +template <typename Container> +::std::function<::std::ostream&(::std::ostream&)> joiner(const Container& container, + const char* sep) { + using std::begin; + using std::end; + const auto beginIter = begin(container); + const auto endIter = end(container); + return [beginIter, endIter, sep](::std::ostream& out) -> ::std::ostream& { + for (auto iter = beginIter; iter != endIter; ++iter) { + if (iter != beginIter) { out << sep; } out << *iter; @@ -242,7 +246,7 @@ private: const iterator mEnd; }; -inline Tokenizer tokenize(StringPiece str, char sep) { +inline Tokenizer tokenize(const StringPiece& str, char sep) { return Tokenizer(str, sep); } @@ -281,7 +285,7 @@ bool extractResFilePathParts(const StringPiece& path, StringPiece* outPrefix, * In the aapt namespace for lookup. */ inline ::std::ostream& operator<<(::std::ostream& out, - ::std::function<::std::ostream&(::std::ostream&)> f) { + const ::std::function<::std::ostream&(::std::ostream&)>& f) { return f(out); } diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h index ee51b360feb4..a24d109abf34 100644 --- a/tools/aapt2/xml/XmlPullParser.h +++ b/tools/aapt2/xml/XmlPullParser.h @@ -60,7 +60,7 @@ public: static bool skipCurrentElement(XmlPullParser* parser); static bool isGoodEvent(Event event); - XmlPullParser(std::istream& in); + explicit XmlPullParser(std::istream& in); ~XmlPullParser(); /** diff --git a/tools/split-select/SplitSelector.h b/tools/split-select/SplitSelector.h index 193fda7077d3..120354f608f3 100644 --- a/tools/split-select/SplitSelector.h +++ b/tools/split-select/SplitSelector.h @@ -29,7 +29,7 @@ namespace split { class SplitSelector { public: SplitSelector(); - SplitSelector(const android::Vector<SplitDescription>& splits); + explicit SplitSelector(const android::Vector<SplitDescription>& splits); android::Vector<SplitDescription> getBestSplits(const SplitDescription& target) const; diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index b134cf7a00a4..2b73facfda3e 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1143,7 +1143,7 @@ public class WifiManager { /** * @return true if this adapter supports Neighbour Awareness Network APIs - * @hide PROPOSED_NAN_API + * @hide */ public boolean isNanSupported() { return isFeatureSupported(WIFI_FEATURE_NAN); diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java index ff612fe779a2..2c87f1122a05 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanManager.java +++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java @@ -254,7 +254,7 @@ public class WifiNanManager { try { mService.enableUsage(); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -269,7 +269,7 @@ public class WifiNanManager { try { mService.disableUsage(); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -283,10 +283,8 @@ public class WifiNanManager { try { return mService.isUsageEnabled(); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } - - return false; } /** @@ -329,7 +327,7 @@ public class WifiNanManager { } catch (RemoteException e) { mClientId = INVALID_CLIENT_ID; mLooper = null; - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } } @@ -364,7 +362,7 @@ public class WifiNanManager { try { mService.disconnect(clientId, binder); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -407,7 +405,7 @@ public class WifiNanManager { mService.publish(clientId, publishConfig, new WifiNanSessionCallbackProxy(this, looper, true, callback)); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -429,7 +427,7 @@ public class WifiNanManager { try { mService.updatePublish(clientId, sessionId, publishConfig); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -469,7 +467,7 @@ public class WifiNanManager { mService.subscribe(clientId, subscribeConfig, new WifiNanSessionCallbackProxy(this, looper, false, callback)); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -495,7 +493,7 @@ public class WifiNanManager { try { mService.updateSubscribe(clientId, sessionId, subscribeConfig); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -519,7 +517,7 @@ public class WifiNanManager { try { mService.terminateSession(clientId, sessionId); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -546,7 +544,7 @@ public class WifiNanManager { try { mService.sendMessage(clientId, sessionId, peerId, message, messageId, retryCount); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -575,7 +573,7 @@ public class WifiNanManager { rangingKey = mService.startRanging(clientId, sessionId, new RttManager.ParcelableRttParams(params)); } catch (RemoteException e) { - e.rethrowAsRuntimeException(); + throw e.rethrowFromSystemServer(); } synchronized (mLock) { |