summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java6
-rw-r--r--core/java/android/content/pm/IntentFilterVerificationInfo.java8
-rw-r--r--core/java/android/os/Build.java2
-rw-r--r--core/java/android/os/HwBinder.java59
-rw-r--r--core/java/android/os/HwParcel.java120
-rw-r--r--core/java/android/os/HwRemoteBinder.java56
-rw-r--r--core/java/android/os/IHwBinder.java28
-rw-r--r--core/java/android/os/IHwInterface.java22
-rw-r--r--core/java/android/view/WindowManager.java9
-rw-r--r--core/java/com/android/internal/util/StateMachine.java19
-rw-r--r--core/jni/Android.mk7
-rw-r--r--core/jni/AndroidRuntime.cpp6
-rw-r--r--core/jni/android_os_HwBinder.cpp288
-rw-r--r--core/jni/android_os_HwBinder.h60
-rw-r--r--core/jni/android_os_HwParcel.cpp913
-rw-r--r--core/jni/android_os_HwParcel.h75
-rw-r--r--core/jni/android_os_HwRemoteBinder.cpp196
-rw-r--r--core/jni/android_os_HwRemoteBinder.h60
-rw-r--r--core/jni/hwbinder/EphemeralStorage.cpp178
-rw-r--r--core/jni/hwbinder/EphemeralStorage.h84
-rwxr-xr-xdocs/html/guide/topics/renderscript/compute.jd1161
-rw-r--r--docs/html/training/implementing-navigation/nav-drawer.jd2
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-az-rAZ/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-be-rBY/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-bn-rBD/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-bs-rBA/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-et-rEE/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-eu-rES/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-gl-rES/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-gu-rIN/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-hy-rAM/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-is-rIS/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ka-rGE/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-kk-rKZ/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-km-rKH/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-kn-rIN/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ky-rKG/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-lo-rLA/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-mk-rMK/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ml-rIN/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-mn-rMN/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-mr-rIN/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ms-rMY/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-my-rMM/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ne-rNP/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-pa-rIN/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-si-rLK/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-sq-rAL/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ta-rIN/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-te-rIN/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ur-rPK/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-uz-rUZ/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml2
-rw-r--r--packages/SystemUI/res/layout/remote_input.xml1
-rw-r--r--packages/SystemUI/res/values-th/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooter.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java21
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java32
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java99
-rw-r--r--services/core/java/com/android/server/pm/PendingPackage.java35
-rw-r--r--services/core/java/com/android/server/pm/SettingBase.java17
-rw-r--r--services/core/java/com/android/server/pm/Settings.java107
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java10
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java37
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java17
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java30
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java3
-rw-r--r--services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java4
-rw-r--r--services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java3
-rw-r--r--telecomm/java/android/telecom/RemoteConnectionService.java1
-rw-r--r--tools/aapt/Resource.cpp17
-rw-r--r--tools/aapt2/AppInfo.h10
-rw-r--r--tools/aapt2/Diagnostics.h6
-rw-r--r--tools/aapt2/Main.cpp2
-rw-r--r--tools/aapt2/NameMangler.h2
-rw-r--r--tools/aapt2/ResourceParser.cpp21
-rw-r--r--tools/aapt2/ResourceUtils.cpp50
-rw-r--r--tools/aapt2/ResourceUtils.h33
-rw-r--r--tools/aapt2/ResourceUtils_test.cpp36
-rw-r--r--tools/aapt2/ResourceValues.cpp10
-rw-r--r--tools/aapt2/Source.h2
-rw-r--r--tools/aapt2/ValueVisitor.h3
-rw-r--r--tools/aapt2/compile/Png.h2
-rw-r--r--tools/aapt2/compile/Pseudolocalizer.h2
-rw-r--r--tools/aapt2/compile/XmlIdCollector.cpp2
-rw-r--r--tools/aapt2/flatten/TableFlattener.h2
-rw-r--r--tools/aapt2/io/FileSystem.h4
-rw-r--r--tools/aapt2/io/ZipArchive.h2
-rw-r--r--tools/aapt2/java/ClassDefinition.h2
-rw-r--r--tools/aapt2/link/Link.cpp383
-rw-r--r--tools/aapt2/link/ManifestFixer.h2
-rw-r--r--tools/aapt2/link/ProductFilter.h2
-rw-r--r--tools/aapt2/link/ReferenceLinker.cpp2
-rw-r--r--tools/aapt2/link/XmlReferenceLinker.cpp2
-rw-r--r--tools/aapt2/process/SymbolTable.h2
-rw-r--r--tools/aapt2/readme.md9
-rw-r--r--tools/aapt2/split/TableSplitter.cpp11
-rw-r--r--tools/aapt2/split/TableSplitter.h2
-rw-r--r--tools/aapt2/split/TableSplitter_test.cpp69
-rw-r--r--tools/aapt2/test/Builders.h28
-rw-r--r--tools/aapt2/test/Common.h2
-rw-r--r--tools/aapt2/test/Context.h2
-rw-r--r--tools/aapt2/util/BigBuffer.h2
-rw-r--r--tools/aapt2/util/Files.h2
-rw-r--r--tools/aapt2/util/Maybe.h8
-rw-r--r--tools/aapt2/util/StringPiece.h4
-rw-r--r--tools/aapt2/util/Util.h20
-rw-r--r--tools/aapt2/xml/XmlPullParser.h2
-rw-r--r--tools/split-select/SplitSelector.h2
166 files changed, 4182 insertions, 570 deletions
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/os/Build.java b/core/java/android/os/Build.java
index 91fe3524e1ad..c180c7f5ef4d 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;
}
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..88af4fb13710
--- /dev/null
+++ b/core/java/android/os/IHwBinder.java
@@ -0,0 +1,28 @@
+/*
+ * 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 {
+ // MUST match libhwbinder/IBinder.h definition !!!
+ public static final int FIRST_CALL_TRANSACTION = 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/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1b37ed47c392..eb6b17e08f46 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1581,6 +1581,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/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/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_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/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/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>&lt;project_root&gt;/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>&hellip;,</i> <i>inNType</i> in<i>N</i>
+ <i>[, specialArguments]</i>) { &hellip; }
+</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 &lt; 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>&hellip;,</i>
+ Allocation ain<i>N</i>);
+
+// Method 2
+public <i>javaFutureType</i> reduce_<i>kernelName</i>(Allocation ain1, <i>&hellip;,</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, &hellip;,
+ <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>&hellip;</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>&hellip;</i>);
+typeBuilder.setY(<i>&hellip;</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() { &hellip; }
+ }
+}
+</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() { &hellip; }
+ }
+
+ // for kernels with int[10] result
+ public static class resultArray10_int {
+ public int[] get() { &hellip; }
+ }
+
+ // 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() { &hellip; }
+ }
+
+ // 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() { &hellip; }
+ }
+
+ // 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() { &hellip; }
+ }
+
+ // 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() { &hellip; }
+ }
+
+ // 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() { &hellip; }
+ }
+
+ // 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() { &hellip; }
+ }
+}
+</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/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 {
&#64;Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ public void onItemClick(AdapterView&lt;?> parent, View view, int position, long id) {
selectItem(position);
}
}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index c16e08a7c428..0ca36f6a89b8 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 6e9dcd7fc193..d40691cd9267 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index ee579dc30b00..12b9302d9100 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml
index 60d0169be4b0..296725f6a0c2 100644
--- a/packages/SettingsLib/res/values-az-rAZ/strings.xml
+++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</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..fb0bf542a6e4 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-be-rBY/strings.xml b/packages/SettingsLib/res/values-be-rBY/strings.xml
index 03de8bab32c1..e9c7f9b8b10a 100644
--- a/packages/SettingsLib/res/values-be-rBY/strings.xml
+++ b/packages/SettingsLib/res/values-be-rBY/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index f6596dc48add..f5e13866b92e 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml
index 4a11198be5f9..614637228421 100644
--- a/packages/SettingsLib/res/values-bn-rBD/strings.xml
+++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-bs-rBA/strings.xml b/packages/SettingsLib/res/values-bs-rBA/strings.xml
index 40a3630a4e55..6426ac808f3c 100644
--- a/packages/SettingsLib/res/values-bs-rBA/strings.xml
+++ b/packages/SettingsLib/res/values-bs-rBA/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 99850a5d5cd4..46baecd0ca0a 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 361ba1f0cef2..8743c0fa4052 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 98f41fc8945d..9c7e76c74552 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index c9da6c96c0ff..c878d93ee043 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -341,4 +341,6 @@
<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 &amp; Feedback"</string>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 05e4e64c383f..bda970a0c848 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 799802b86b3a..0f41d1991f61 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -341,4 +341,6 @@
<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 &amp; feedback"</string>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 799802b86b3a..0f41d1991f61 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -341,4 +341,6 @@
<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 &amp; feedback"</string>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 799802b86b3a..0f41d1991f61 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -341,4 +341,6 @@
<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 &amp; feedback"</string>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 08e739a00f50..aa98329237c7 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 3d2f6c132da0..23e01eab7f74 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml
index a7b0f64f9a4d..6b98a193b71b 100644
--- a/packages/SettingsLib/res/values-et-rEE/strings.xml
+++ b/packages/SettingsLib/res/values-et-rEE/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml
index f476511b8106..c7525fbe7206 100644
--- a/packages/SettingsLib/res/values-eu-rES/strings.xml
+++ b/packages/SettingsLib/res/values-eu-rES/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 44918513bcd7..ab4f70b14972 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index cdef968d90aa..d2d6959ac3bc 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 958b8fd4d5d3..8230f87088af 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index cf08f3b5f237..bad9ab28a389 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml
index cdc4581ca80c..ed0d3f33448a 100644
--- a/packages/SettingsLib/res/values-gl-rES/strings.xml
+++ b/packages/SettingsLib/res/values-gl-rES/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml
index 1b40e01737d1..79014b6a2ba3 100644
--- a/packages/SettingsLib/res/values-gu-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 8dfbb4a58780..5edb8283088b 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 07e492581d41..803a995ed149 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 0d8ec26eb668..328fa2dfc877 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
index cb12fbfaf502..d51527496587 100644
--- a/packages/SettingsLib/res/values-hy-rAM/strings.xml
+++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index ffdb60745571..c56d58ded60e 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -341,4 +341,6 @@
<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 &amp; masukan"</string>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml
index 25e13a4d69d8..5ce6be45cde5 100644
--- a/packages/SettingsLib/res/values-is-rIS/strings.xml
+++ b/packages/SettingsLib/res/values-is-rIS/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 2907fabef70e..24ec27786ca0 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 7c16e4276347..f2235655dd0f 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 25f4152a22fa..f356b95b5c08 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -343,4 +343,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml
index ffe194a0bc29..22a1f8a81228 100644
--- a/packages/SettingsLib/res/values-ka-rGE/strings.xml
+++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
index 3dcc7eb50ced..30bae8bf5040 100644
--- a/packages/SettingsLib/res/values-kk-rKZ/strings.xml
+++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml
index aa0ce2498737..8daf33862a99 100644
--- a/packages/SettingsLib/res/values-km-rKH/strings.xml
+++ b/packages/SettingsLib/res/values-km-rKH/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml
index 643875fa6ea0..8ca9035630e2 100644
--- a/packages/SettingsLib/res/values-kn-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index aaf34631b7fb..47781458516e 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -340,5 +340,7 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml
index 96c1ec024aa6..eff06a47ddc8 100644
--- a/packages/SettingsLib/res/values-ky-rKG/strings.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml
index 24f0c16c4962..87449fab6ec2 100644
--- a/packages/SettingsLib/res/values-lo-rLA/strings.xml
+++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml
@@ -341,4 +341,6 @@
<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">"ຊ່ວຍເຫຼືອ &amp; ຄຳຕິຊົມ"</string>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 4b8890d04d33..fa4812a8a1c1 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 4d76adc3e291..568ab4c02b68 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml
index 953360a7aa00..991d60c92a62 100644
--- a/packages/SettingsLib/res/values-mk-rMK/strings.xml
+++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml
index 92c04487ace2..38d56e0e0917 100644
--- a/packages/SettingsLib/res/values-ml-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml
index 4c98c04c07d4..beb80e89c283 100644
--- a/packages/SettingsLib/res/values-mn-rMN/strings.xml
+++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml
index e3a7cc46cd44..10aa5601709f 100644
--- a/packages/SettingsLib/res/values-mr-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml
index a1caa2a94419..5c66ddb707ad 100644
--- a/packages/SettingsLib/res/values-ms-rMY/strings.xml
+++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml
@@ -341,4 +341,6 @@
<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 &amp; maklum balas"</string>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index 6a1c33190486..4f5ea65e47d0 100644
--- a/packages/SettingsLib/res/values-my-rMM/strings.xml
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 5d8ae55fb592..1a0761400e2b 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml
index 15cc8ea87c72..e73b07ec3903 100644
--- a/packages/SettingsLib/res/values-ne-rNP/strings.xml
+++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 12bdb4f30c06..83b6c7414f46 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml
index 21d11b00cd4b..47ed7e37d316 100644
--- a/packages/SettingsLib/res/values-pa-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index d5116a2e3d7c..1f07580ff1e2 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index f0cfa23df334..dca0f8efd88d 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 40dfc74806b3..d5910aca98e9 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index f0cfa23df334..dca0f8efd88d 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 6cc0f87e1b31..cfc0ea8fa69a 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index ad4db89418b3..38c8f0eac7a8 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml
index 5efb4001a638..d73df8a31b5d 100644
--- a/packages/SettingsLib/res/values-si-rLK/strings.xml
+++ b/packages/SettingsLib/res/values-si-rLK/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 7a7e3d49d12d..10413d681b68 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 10bff6e038a9..dba54539abbe 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml
index e4f0eaa29596..326e8eab55a3 100644
--- a/packages/SettingsLib/res/values-sq-rAL/strings.xml
+++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index fba58c50211c..bb1515db36c0 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index ecc728a81eec..ecbdd91325ca 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 435a1bdd99be..b68dcb7c94e4 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml
index 033955c77e91..03623bf592c3 100644
--- a/packages/SettingsLib/res/values-ta-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml
@@ -341,4 +341,6 @@
<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">"உதவி &amp; கருத்து"</string>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml
index 57581765ae4e..ec6aa02b8ec6 100644
--- a/packages/SettingsLib/res/values-te-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-te-rIN/strings.xml
@@ -341,4 +341,6 @@
<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">"సహాయం &amp; అభిప్రాయం"</string>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 4849e1955321..01348e0b984d 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 24f5499d4490..b58fb3156e7e 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 950e3221ff7c..f50eeb046b32 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 6b49b14084e9..1ba10553da41 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml
index 2ac8a6145797..4211dd239407 100644
--- a/packages/SettingsLib/res/values-ur-rPK/strings.xml
+++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index 9022ad31dacf..af30901ac8da 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index c58f84993bb6..bb038e39d3cb 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 18ae80e3d9b6..c4468e6e0ef1 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 660071df7073..498988c1e0ec 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index bce6f244d1e4..69999c1f8629 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 07c6fce2c3f9..ccf607a59fb4 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -341,4 +341,6 @@
<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>
+ <!-- no translation found for content_description_menu_button (8182594799812351266) -->
+ <skip />
</resources>
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/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/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index e95cc6b3fed3..8db7c37b8fa7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3431,7 +3431,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(
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..ea9ec5f46fdd 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));
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4994d6bc9206..ab83c11bd8e6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -364,6 +364,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;
@@ -801,10 +802,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();
}
}
@@ -2873,7 +2873,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 +2882,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 +2902,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);
@@ -8164,12 +8165,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");
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..7083ab7432bc 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;
@@ -7829,14 +7830,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/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 216afe610261..c952ee65dc81 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,19 @@ 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();
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+ synchronized (mDeviceLockedForUser) {
+ mDeviceLockedForUser.put(userId, locked);
+ }
+ if (locked) {
+ try {
+ ActivityManagerNative.getDefault().notifyLockedProfile(userId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
}
@Override
@@ -952,9 +946,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/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index d3dab440c44a..05c05b159a78 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() {
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..19ad5e4563aa 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5776,6 +5776,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..c83c083b2ec0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1325,7 +1325,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());
}
/**
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/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/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/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/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..5a2bb6ad9a72 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;
@@ -77,7 +80,13 @@ struct LinkOptions {
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;
};
@@ -585,7 +594,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 +606,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 +707,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 +822,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 +1234,94 @@ 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.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 +1330,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 +1404,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 +1448,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());
+ }
- if (mOptions.staticLib) {
- if (!mOptions.products.empty()) {
- mContext->getDiagnostics()->warn(
- DiagMessage() << "can't select products when building static library");
+ 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));
+ }
- if (mOptions.tableSplitterOptions.configFilter != nullptr ||
- mOptions.tableSplitterOptions.preferredDensity) {
- mContext->getDiagnostics()->warn(
- DiagMessage() << "can't strip resources when building static library");
+ TableSplitter tableSplitter(adjustedConstraintsList, mOptions.tableSplitterOptions);
+ if (!tableSplitter.verifySplitConstraints(mContext)) {
+ return 1;
+ }
+ tableSplitter.splitTable(&mFinalTable);
+
+ // 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, ", ") << "'");
}
- } else {
- ProductFilter productFilter(mOptions.products);
- if (!productFilter.consume(mContext, &mFinalTable)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
+
+ std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter(*pathIter);
+ if (!archiveWriter) {
+ mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive");
+ return 1;
+ }
+
+ // 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 +1567,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.
@@ -1382,13 +1594,6 @@ public:
error = true;
}
}
-
- const bool keepRawValues = mOptions.staticLib;
- bool result = flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
- keepRawValues, archiveWriter.get(), mContext);
- if (!result) {
- error = true;
- }
} else {
error = true;
}
@@ -1399,58 +1604,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 +1695,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",
@@ -1623,6 +1781,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 +1902,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/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/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;